mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
Replaces the mailto: link with a server-side send, so the message carries the store's signature and logo instead of depending on each workstation's Outlook. Store record (groups): - address, phone and logo (data URI, 200 KB cap) feed the mail signature - per-store SMTP settings: host, port, SSL/STARTTLS, credentials, sender address and display name, with an enable switch - "test connection" button verifies the server without sending anything - the empty-form literal, previously repeated five times, becomes one constant Sending: - nodemailer transport built per store from its own settings - multipart mail: plain-text alternative plus HTML whose signature embeds the logo as an inline CID attachment, which Outlook renders without the remote image blocking that a data: URI would hit - delivery details are HTML-escaped - Reply-To set to the store address; the row shows a spinner and refuses a second click while a send is in flight Credentials: - the SMTP password is never returned to the client; a response-layer sanitizer strips it from every /api payload and replaces it with a smtpPasswordSet flag, covering the ten-plus queries that join full group rows into deliveries, orders and user relations - an empty password field on save keeps the stored one rather than clearing it Verified end-to-end against a local SMTP server: transport, auth, From/Reply-To, multipart structure and the inline logo attachment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHdE9nEh8zHhQNGpCgrtYe
91 lines
3.7 KiB
TypeScript
91 lines
3.7 KiB
TypeScript
import { Client } from 'pg';
|
|
|
|
export async function runProductionMigrations() {
|
|
const databaseUrl = process.env.DATABASE_URL;
|
|
if (!databaseUrl) {
|
|
console.error('❌ MIGRATION: DATABASE_URL not found for migrations');
|
|
return;
|
|
}
|
|
|
|
console.log('🔄 MIGRATION: Starting SAV production migrations...');
|
|
console.log('🔄 MIGRATION: Database URL configured:', databaseUrl.substring(0, 30) + '...');
|
|
|
|
const client = new Client({
|
|
connectionString: databaseUrl,
|
|
ssl: false, // No SSL for local Docker
|
|
});
|
|
|
|
try {
|
|
await client.connect();
|
|
console.log('✅ MIGRATION: Connected to PostgreSQL database');
|
|
|
|
console.log('🔄 MIGRATION: Checking if priority column exists...');
|
|
|
|
// Vérifier si la colonne priority existe
|
|
const checkPriorityColumn = await client.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name='sav_tickets' AND column_name='priority'
|
|
`);
|
|
|
|
console.log('🔄 MIGRATION: Priority column check result:', checkPriorityColumn.rows.length);
|
|
|
|
if (checkPriorityColumn.rows.length === 0) {
|
|
console.log('📝 MIGRATION: Priority column missing, adding it now...');
|
|
|
|
// Ajouter seulement la colonne priority manquante
|
|
await client.query(`
|
|
ALTER TABLE sav_tickets ADD COLUMN priority varchar(50) NOT NULL DEFAULT 'normale';
|
|
`);
|
|
console.log('✅ MIGRATION: Priority column added successfully!');
|
|
|
|
// Créer les index pour optimiser les performances
|
|
await client.query(`
|
|
CREATE INDEX IF NOT EXISTS idx_sav_tickets_priority ON sav_tickets(priority);
|
|
`);
|
|
console.log('✅ MIGRATION: Priority index created successfully!');
|
|
|
|
// Vérification finale
|
|
const verificationResult = await client.query(`
|
|
SELECT
|
|
COUNT(*) as total_records,
|
|
COUNT(CASE WHEN priority IS NOT NULL THEN 1 END) as records_with_priority
|
|
FROM sav_tickets;
|
|
`);
|
|
|
|
console.log('✅ MIGRATION: SAV migration completed successfully!');
|
|
console.log('📊 MIGRATION: Verification result:', verificationResult.rows[0]);
|
|
|
|
} else {
|
|
console.log('✅ MIGRATION: Priority column already exists, skipping migration');
|
|
}
|
|
|
|
// Coordonnées magasin + configuration SMTP par magasin (mails fournisseurs)
|
|
console.log('🔄 MIGRATION: Ensuring store contact and SMTP columns on groups...');
|
|
await client.query(`
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS address TEXT;
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS phone VARCHAR(50);
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS logo TEXT;
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS smtp_enabled BOOLEAN DEFAULT false;
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS smtp_host VARCHAR(255);
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS smtp_port INTEGER;
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS smtp_secure BOOLEAN DEFAULT false;
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS smtp_user VARCHAR(255);
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS smtp_password VARCHAR(255);
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS smtp_sender_email VARCHAR(255);
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS smtp_sender_name VARCHAR(255);
|
|
`);
|
|
console.log('✅ MIGRATION: Store contact and SMTP columns are present on groups');
|
|
|
|
} catch (error) {
|
|
console.error('❌ MIGRATION ERROR: Failed to run SAV production migrations:', error);
|
|
console.error('❌ MIGRATION ERROR: Error details:', {
|
|
message: (error as any)?.message,
|
|
code: (error as any)?.code,
|
|
stack: (error as any)?.stack
|
|
});
|
|
// Ne pas faire échouer le démarrage du serveur pour les erreurs de migration
|
|
} finally {
|
|
await client.end();
|
|
console.log('🔌 MIGRATION: Database connection closed');
|
|
}
|
|
} |