diff --git a/server/db.production.ts b/server/db.production.ts index accea2f..dbebe98 100644 --- a/server/db.production.ts +++ b/server/db.production.ts @@ -1,6 +1,7 @@ import { Pool } from 'pg'; import { drizzle } from 'drizzle-orm/node-postgres'; import * as schema from "@shared/schema"; +import { runProductionMigrations } from './migrations.production.js'; console.log('🐳 PRODUCTION: Configuring standard PostgreSQL database'); @@ -36,4 +37,16 @@ pool.on('error', (err) => { console.error('❌ PostgreSQL connection error:', err); }); +// Run migrations automatically when database is configured +(async () => { + try { + console.log('🔄 Running automatic database migrations...'); + await runProductionMigrations(); + console.log('✅ Database migrations completed'); + } catch (error) { + console.error('❌ Database migrations failed:', error); + // Don't fail the application startup for migration errors + } +})(); + console.log('✅ Production PostgreSQL database configured'); \ No newline at end of file diff --git a/server/index.production.ts b/server/index.production.ts index f3d8515..0a20be1 100644 --- a/server/index.production.ts +++ b/server/index.production.ts @@ -6,6 +6,7 @@ import { dirname, join } from 'path'; // Database will be configured in storage.js import { storage } from "./storage.js"; import { setupLocalAuth, requireAuth } from "./localAuth.production.js"; +import { runProductionMigrations } from "./migrations.production.js"; // Get directory paths const __filename = fileURLToPath(import.meta.url); @@ -43,6 +44,10 @@ import { registerRoutes } from "./routes.js"; async function registerProductionRoutes(app: Express): Promise { console.log('🔧 Registering all production routes...'); + // Run automatic migrations before registering routes + console.log('🔄 Running automatic production migrations...'); + await runProductionMigrations(); + // Register ALL API routes (same as development) await registerRoutes(app); console.log('✅ All routes registered successfully for production'); diff --git a/server/migrations.production.ts b/server/migrations.production.ts new file mode 100644 index 0000000..7deed89 --- /dev/null +++ b/server/migrations.production.ts @@ -0,0 +1,159 @@ +import { neon } from '@neondatabase/serverless'; + +export async function runProductionMigrations() { + const databaseUrl = process.env.DATABASE_URL; + if (!databaseUrl) { + console.error('❌ DATABASE_URL not found for migrations'); + return; + } + + const sql = neon(databaseUrl); + + try { + console.log('🔄 Checking and running SAV production migrations...'); + + // VĂ©rifier si la colonne priority existe + const checkPriorityColumn = await sql` + SELECT 1 FROM information_schema.columns + WHERE table_name='sav_tickets' AND column_name='priority' + `; + + if (checkPriorityColumn.length === 0) { + console.log('📝 Running SAV table migrations...'); + + // ExĂ©cuter la migration complĂšte SAV + await sql` + -- Ajouter la colonne priority si elle n'existe pas + DO $$ + BEGIN + -- Ajouter la colonne priority si elle n'existe pas + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='priority') THEN + ALTER TABLE sav_tickets ADD COLUMN priority varchar(50) NOT NULL DEFAULT 'normale'; + RAISE NOTICE 'Colonne priority ajoutĂ©e'; + END IF; + + -- Ajouter la colonne problem_type si elle n'existe pas + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='problem_type') THEN + ALTER TABLE sav_tickets ADD COLUMN problem_type varchar(100) NOT NULL DEFAULT 'defectueux'; + RAISE NOTICE 'Colonne problem_type ajoutĂ©e'; + END IF; + + -- Ajouter la colonne resolution_description si elle n'existe pas + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='resolution_description') THEN + ALTER TABLE sav_tickets ADD COLUMN resolution_description text; + RAISE NOTICE 'Colonne resolution_description ajoutĂ©e'; + END IF; + + -- Ajouter la colonne resolved_at si elle n'existe pas + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='resolved_at') THEN + ALTER TABLE sav_tickets ADD COLUMN resolved_at timestamp; + RAISE NOTICE 'Colonne resolved_at ajoutĂ©e'; + END IF; + + -- Ajouter la colonne closed_at si elle n'existe pas + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='closed_at') THEN + ALTER TABLE sav_tickets ADD COLUMN closed_at timestamp; + RAISE NOTICE 'Colonne closed_at ajoutĂ©e'; + END IF; + + -- VĂ©rifier si la colonne status existe, sinon l'ajouter + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='status') THEN + ALTER TABLE sav_tickets ADD COLUMN status varchar(50) NOT NULL DEFAULT 'nouveau'; + RAISE NOTICE 'Colonne status ajoutĂ©e'; + END IF; + + END $$; + `; + + // CrĂ©er la table sav_ticket_history si elle n'existe pas + await sql` + CREATE TABLE IF NOT EXISTS sav_ticket_history ( + id serial PRIMARY KEY, + ticket_id integer NOT NULL, + action varchar(100) NOT NULL, + description text, + created_by varchar(255) NOT NULL, + created_at timestamp DEFAULT NOW() + ); + `; + + // Ajouter les contraintes de clĂ© Ă©trangĂšre si elles n'existent pas + await sql` + DO $$ + BEGIN + -- VĂ©rifier et ajouter la contrainte FK vers suppliers + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name='sav_tickets_supplier_id_fkey') THEN + ALTER TABLE sav_tickets ADD CONSTRAINT sav_tickets_supplier_id_fkey + FOREIGN KEY (supplier_id) REFERENCES suppliers(id); + RAISE NOTICE 'Contrainte FK supplier_id ajoutĂ©e'; + END IF; + + -- VĂ©rifier et ajouter la contrainte FK vers groups + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name='sav_tickets_group_id_fkey') THEN + ALTER TABLE sav_tickets ADD CONSTRAINT sav_tickets_group_id_fkey + FOREIGN KEY (group_id) REFERENCES groups(id); + RAISE NOTICE 'Contrainte FK group_id ajoutĂ©e'; + END IF; + + -- VĂ©rifier et ajouter la contrainte FK pour sav_ticket_history + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name='sav_ticket_history_ticket_id_fkey') THEN + ALTER TABLE sav_ticket_history ADD CONSTRAINT sav_ticket_history_ticket_id_fkey + FOREIGN KEY (ticket_id) REFERENCES sav_tickets(id) ON DELETE CASCADE; + RAISE NOTICE 'Contrainte FK history ticket_id ajoutĂ©e'; + END IF; + + END $$; + `; + + // CrĂ©er les index pour optimiser les performances + await sql` + CREATE INDEX IF NOT EXISTS idx_sav_tickets_status ON sav_tickets(status); + CREATE INDEX IF NOT EXISTS idx_sav_tickets_priority ON sav_tickets(priority); + CREATE INDEX IF NOT EXISTS idx_sav_tickets_supplier_id ON sav_tickets(supplier_id); + CREATE INDEX IF NOT EXISTS idx_sav_tickets_group_id ON sav_tickets(group_id); + CREATE INDEX IF NOT EXISTS idx_sav_tickets_created_at ON sav_tickets(created_at); + CREATE INDEX IF NOT EXISTS idx_sav_ticket_history_ticket_id ON sav_ticket_history(ticket_id); + `; + + // Mettre Ă  jour les valeurs existantes si nĂ©cessaire + await sql` + UPDATE sav_tickets + SET priority = 'normale' + WHERE priority IS NULL OR priority = ''; + `; + + await sql` + UPDATE sav_tickets + SET problem_type = 'defectueux' + WHERE problem_type IS NULL OR problem_type = ''; + `; + + console.log('✅ SAV production migrations completed successfully!'); + + // VĂ©rification finale + const verificationResult = await sql` + SELECT + 'sav_tickets' as table_name, + COUNT(*) as record_count, + COUNT(CASE WHEN priority IS NOT NULL THEN 1 END) as records_with_priority + FROM sav_tickets + UNION ALL + SELECT + 'sav_ticket_history' as table_name, + COUNT(*) as record_count, + NULL as records_with_priority + FROM sav_ticket_history; + `; + + console.log('📊 SAV tables status:', verificationResult); + + } else { + console.log('✅ SAV migrations already applied, skipping'); + } + + } catch (error) { + console.error('❌ Error running SAV production migrations:', error); + // Ne pas faire Ă©chouer le dĂ©marrage du serveur pour les erreurs de migration + // L'application peut continuer Ă  fonctionner mĂȘme si la migration Ă©choue + } +} \ No newline at end of file