diff --git a/Dockerfile b/Dockerfile index aafafbe..7c3eba8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -81,8 +81,9 @@ RUN npm ci --only=production && npm cache clean --force COPY --from=build --chown=nextjs:nodejs /app/dist ./dist COPY --from=build --chown=nextjs:nodejs /app/shared ./shared -# Copy migration scripts +# Copy migration scripts and files COPY --chown=nextjs:nodejs scripts/ ./scripts/ +COPY --chown=nextjs:nodejs migrations/ ./migrations/ # Create symlink for public files to be accessible from dist RUN ln -sf /app/dist/public /app/dist/public diff --git a/attached_assets/Pasted--at-app-node-modules-pg-pool-index-js-45-11-at-process-processTicksAndRejection-1755508892611_1755508892611.txt b/attached_assets/Pasted--at-app-node-modules-pg-pool-index-js-45-11-at-process-processTicksAndRejection-1755508892611_1755508892611.txt new file mode 100644 index 0000000..87b8661 --- /dev/null +++ b/attached_assets/Pasted--at-app-node-modules-pg-pool-index-js-45-11-at-process-processTicksAndRejection-1755508892611_1755508892611.txt @@ -0,0 +1,204 @@ + + + at /app/node_modules/pg-pool/index.js:45:11 + + at process.processTicksAndRejections (node:internal/process/task_queues:95:5) + + at async file:///app/node_modules/drizzle-orm/node-postgres/session.js:83:22 + + at async DatabaseStorage.getDlcProducts (file:///app/dist/index.js:2187:25) + + at async file:///app/dist/index.js:6178:28 { + + length: 124, + + severity: 'ERROR', + + code: '42703', + + detail: undefined, + + hint: undefined, + + position: '337', + + internalPosition: undefined, + + internalQuery: undefined, + + where: undefined, + + schema: undefined, + + table: undefined, + + column: undefined, + + dataType: undefined, + + constraint: undefined, + + file: 'parse_relation.c', + + line: '3665', + + routine: 'errorMissingColumn' + +} + +GET /api/dlc-products 500 in 20ms + +GET /api/dlc-products/stats 200 in 10ms + +Error fetching DLC products: error: column dlc_products.stock_epuise does not exist + + at /app/node_modules/pg-pool/index.js:45:11 + + at process.processTicksAndRejections (node:internal/process/task_queues:95:5) + + at async file:///app/node_modules/drizzle-orm/node-postgres/session.js:83:22 + + at async DatabaseStorage.getDlcProducts (file:///app/dist/index.js:2187:25) + + at async file:///app/dist/index.js:6178:28 { + + length: 124, + + severity: 'ERROR', + + code: '42703', + + detail: undefined, + + hint: undefined, + + position: '337', + + internalPosition: undefined, + + internalQuery: undefined, + + where: undefined, + + schema: undefined, + + table: undefined, + + column: undefined, + + dataType: undefined, + + constraint: undefined, + + file: 'parse_relation.c', + + line: '3665', + + routine: 'errorMissingColumn' + +} + +GET /api/dlc-products 500 in 17ms + +Error fetching DLC products: error: column dlc_products.stock_epuise does not exist + + at /app/node_modules/pg-pool/index.js:45:11 + + at process.processTicksAndRejections (node:internal/process/task_queues:95:5) + + at async file:///app/node_modules/drizzle-orm/node-postgres/session.js:83:22 + + at async DatabaseStorage.getDlcProducts (file:///app/dist/index.js:2187:25) + + at async file:///app/dist/index.js:6178:28 { + + length: 124, + + severity: 'ERROR', + + code: '42703', + + detail: undefined, + + hint: undefined, + + position: '337', + + internalPosition: undefined, + + internalQuery: undefined, + + where: undefined, + + schema: undefined, + + table: undefined, + + column: undefined, + + dataType: undefined, + + constraint: undefined, + + file: 'parse_relation.c', + + line: '3665', + + routine: 'errorMissingColumn' + +} + +GET /api/dlc-products 500 in 12ms + +Error fetching DLC products: error: column dlc_products.stock_epuise does not exist + + at /app/node_modules/pg-pool/index.js:45:11 + + at process.processTicksAndRejections (node:internal/process/task_queues:95:5) + + at async file:///app/node_modules/drizzle-orm/node-postgres/session.js:83:22 + + at async DatabaseStorage.getDlcProducts (file:///app/dist/index.js:2187:25) + + at async file:///app/dist/index.js:6178:28 { + + length: 124, + + severity: 'ERROR', + + code: '42703', + + detail: undefined, + + hint: undefined, + + position: '337', + + internalPosition: undefined, + + internalQuery: undefined, + + where: undefined, + + schema: undefined, + + table: undefined, + + column: undefined, + + dataType: undefined, + + constraint: undefined, + + file: 'parse_relation.c', + + line: '3665', + + routine: 'errorMissingColumn' + +} + +GET /api/dlc-products 500 in 9ms + + + \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 1915673..523516d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,9 @@ services: SESSION_SECRET: LogiFlow_Super_Secret_Session_Key_2025_Production PORT: 3000 USE_LOCAL_AUTH: "true" + POSTGRES_USER: logiflow_admin + POSTGRES_PASSWORD: LogiFlow2025! + POSTGRES_DB: logiflow_db ports: - "3000:3000" networks: @@ -41,6 +44,7 @@ services: volumes: - postgres_data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/01-init.sql + - ./migrations:/migrations healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] interval: 15s diff --git a/migrations/002_add_dlc_stock_epuise.sql b/migrations/002_add_dlc_stock_epuise.sql new file mode 100644 index 0000000..03e213a --- /dev/null +++ b/migrations/002_add_dlc_stock_epuise.sql @@ -0,0 +1,51 @@ +-- Migration 002: Ajout des champs stock épuisé aux produits DLC +-- Date: 2025-08-18 +-- Description: Ajoute la fonctionnalité de gestion du stock épuisé pour distinguer +-- les produits périmés des produits en rupture de stock + +-- Vérification que la table dlc_products existe +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'dlc_products') THEN + RAISE EXCEPTION 'La table dlc_products n''existe pas. Cette migration nécessite que la table soit déjà créée.'; + END IF; +END +$$; + +-- Ajout des nouveaux champs pour la gestion du stock épuisé +ALTER TABLE dlc_products +ADD COLUMN IF NOT EXISTS stock_epuise boolean DEFAULT false NOT NULL, +ADD COLUMN IF NOT EXISTS stock_epuise_by varchar(255), +ADD COLUMN IF NOT EXISTS stock_epuise_at timestamp; + +-- Commentaires pour documenter les nouveaux champs +COMMENT ON COLUMN dlc_products.stock_epuise IS 'Indique si le produit est marqué comme stock épuisé (différent de périmé)'; +COMMENT ON COLUMN dlc_products.stock_epuise_by IS 'ID de l''utilisateur qui a marqué le produit comme stock épuisé'; +COMMENT ON COLUMN dlc_products.stock_epuise_at IS 'Date et heure de marquage du stock épuisé'; + +-- Index pour améliorer les performances sur les requêtes de stock épuisé +CREATE INDEX IF NOT EXISTS idx_dlc_products_stock_epuise ON dlc_products(stock_epuise); + +-- Initialiser les produits existants avec stock disponible (pas épuisé) +-- Cette ligne est sécurisée car DEFAULT false est déjà appliqué +UPDATE dlc_products +SET stock_epuise = false +WHERE stock_epuise IS NULL; + +-- Vérification finale +DO $$ +DECLARE + column_count integer; +BEGIN + SELECT COUNT(*) INTO column_count + FROM information_schema.columns + WHERE table_name = 'dlc_products' + AND column_name IN ('stock_epuise', 'stock_epuise_by', 'stock_epuise_at'); + + IF column_count = 3 THEN + RAISE NOTICE 'Migration 002 réussie: % colonnes ajoutées à dlc_products', column_count; + ELSE + RAISE EXCEPTION 'Migration 002 échouée: seulement % colonnes trouvées sur 3 attendues', column_count; + END IF; +END +$$; \ No newline at end of file diff --git a/scripts/auto-migrate-production.sh b/scripts/auto-migrate-production.sh old mode 100644 new mode 100755 index 82cb74c..24b4995 --- a/scripts/auto-migrate-production.sh +++ b/scripts/auto-migrate-production.sh @@ -1,12 +1,24 @@ #!/bin/bash -# Script de migration automatique - Crée UNIQUEMENT la table announcements -# Toutes les autres tables existent déjà en production +# Script de migration automatique - Production +# Crée la table announcements et ajoute les champs DLC stock épuisé set -e -echo "🔄 [AUTO-MIGRATE] Vérification de la table announcements..." +echo "🔄 [AUTO-MIGRATE] Début des migrations automatiques..." + +# Construire DATABASE_URL si nécessaire +if [ -z "$DATABASE_URL" ]; then + if [ -n "$POSTGRES_USER" ] && [ -n "$POSTGRES_PASSWORD" ] && [ -n "$POSTGRES_DB" ]; then + export DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@logiflow-db:5432/${POSTGRES_DB}" + echo "🔧 [AUTO-MIGRATE] URL de base de données construite" + else + echo "❌ [AUTO-MIGRATE] Variables de base de données manquantes" + exit 1 + fi +fi # Vérifier si la base de données est accessible +echo "🔗 [AUTO-MIGRATE] Test de connexion à la base de données..." if ! psql "$DATABASE_URL" -c "SELECT 1;" > /dev/null 2>&1; then echo "❌ [AUTO-MIGRATE] Impossible de se connecter à la base de données" exit 1 @@ -42,4 +54,29 @@ else echo "ℹ️ [AUTO-MIGRATE] Table announcements existe déjà - aucune action nécessaire" fi +# Vérifier et ajouter les colonnes DLC stock épuisé +echo "🔄 [AUTO-MIGRATE] Vérification des colonnes DLC stock épuisé..." + +if psql "$DATABASE_URL" -tAc "SELECT EXISTS (SELECT FROM information_schema.columns WHERE table_name='dlc_products' AND column_name='stock_epuise');" | grep -q "f"; then + echo "🔧 [AUTO-MIGRATE] Ajout des colonnes stock épuisé à dlc_products..." + psql "$DATABASE_URL" << 'EOF' +-- Migration sécurisée pour ajouter les champs stock épuisé +ALTER TABLE dlc_products +ADD COLUMN IF NOT EXISTS stock_epuise boolean DEFAULT false NOT NULL, +ADD COLUMN IF NOT EXISTS stock_epuise_by varchar(255), +ADD COLUMN IF NOT EXISTS stock_epuise_at timestamp; + +-- Commentaires pour documenter les nouveaux champs +COMMENT ON COLUMN dlc_products.stock_epuise IS 'Indique si le produit est marqué comme stock épuisé (différent de périmé)'; +COMMENT ON COLUMN dlc_products.stock_epuise_by IS 'ID de l''utilisateur qui a marqué le produit comme stock épuisé'; +COMMENT ON COLUMN dlc_products.stock_epuise_at IS 'Date et heure de marquage du stock épuisé'; + +-- Index pour améliorer les performances sur les requêtes de stock épuisé +CREATE INDEX IF NOT EXISTS idx_dlc_products_stock_epuise ON dlc_products(stock_epuise); +EOF + echo "✅ [AUTO-MIGRATE] Colonnes stock épuisé ajoutées avec succès" +else + echo "ℹ️ [AUTO-MIGRATE] Colonnes stock épuisé existent déjà" +fi + echo "✅ [AUTO-MIGRATE] Migration terminée!" \ No newline at end of file diff --git a/scripts/production-deploy-dlc-stock.sh b/scripts/production-deploy-dlc-stock.sh new file mode 100755 index 0000000..126e66a --- /dev/null +++ b/scripts/production-deploy-dlc-stock.sh @@ -0,0 +1,174 @@ +#!/bin/bash + +# Script de déploiement production - Fonctionnalité Stock Épuisé DLC +# Auteur: Système de gestion automatisé +# Date: 2025-08-18 + +set -e + +echo "🚀 [DEPLOY] Déploiement production - Fonctionnalité Stock Épuisé DLC" +echo "==================================================" + +# Fonction de logging avec timestamp +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" +} + +# Vérification des prérequis +log "📋 Vérification des prérequis..." + +if ! command -v docker &> /dev/null; then + log "❌ Docker n'est pas installé" + exit 1 +fi + +if ! command -v docker-compose &> /dev/null; then + log "❌ Docker Compose n'est pas installé" + exit 1 +fi + +if [ ! -f "docker-compose.yml" ]; then + log "❌ Fichier docker-compose.yml non trouvé" + exit 1 +fi + +log "✅ Prérequis validés" + +# Sauvegarde préventive +log "💾 Création d'une sauvegarde préventive..." +timestamp=$(date +%Y%m%d_%H%M%S) +backup_file="backup_pre_dlc_stock_${timestamp}.sql" + +# Tentative de sauvegarde (non bloquante si elle échoue) +if docker-compose exec -T logiflow-db pg_dump -U logiflow_admin logiflow_db > "${backup_file}" 2>/dev/null; then + log "✅ Sauvegarde créée: ${backup_file}" +else + log "⚠️ Sauvegarde échouée - continuons sans sauvegarde" +fi + +# Mode maintenance +log "🛑 Activation du mode maintenance..." +echo "Redirection vers une page de maintenance pendant la mise à jour" > maintenance.html + +# Arrêt de l'application +log "🔴 Arrêt de l'application..." +docker-compose down + +# Rebuild avec les nouvelles fonctionnalités +log "🔨 Reconstruction de l'application avec les nouvelles fonctionnalités..." +docker-compose build --no-cache logiflow + +# Démarrage de la base de données uniquement +log "🗄️ Démarrage de la base de données..." +docker-compose up -d logiflow-db + +# Attendre que PostgreSQL soit prêt +log "⏳ Attente de PostgreSQL (30 secondes)..." +sleep 30 + +# Vérifier la connexion à la base +log "🔗 Test de connexion à la base de données..." +max_attempts=10 +attempt=1 + +while [ $attempt -le $max_attempts ]; do + if docker-compose exec -T logiflow-db pg_isready -U logiflow_admin; then + log "✅ Base de données prête" + break + fi + + if [ $attempt -eq $max_attempts ]; then + log "❌ Impossible de se connecter à la base de données après $max_attempts tentatives" + exit 1 + fi + + log "⏳ Tentative $attempt/$max_attempts - attente 5 secondes..." + sleep 5 + ((attempt++)) +done + +# Exécution manuelle de la migration (au cas où auto-migrate ne fonctionnerait pas) +log "🔄 Exécution de la migration DLC..." +if docker-compose exec -T logiflow-db psql -U logiflow_admin -d logiflow_db -f /migrations/002_add_dlc_stock_epuise.sql; then + log "✅ Migration DLC exécutée avec succès" +else + log "⚠️ Erreur lors de la migration - vérification manuelle nécessaire" +fi + +# Démarrage complet de l'application +log "🚀 Démarrage complet de l'application..." +docker-compose up -d + +# Attendre que l'application soit prête +log "⏳ Attente du démarrage de l'application (45 secondes)..." +sleep 45 + +# Tests de santé +log "🩺 Tests de santé de l'application..." +max_health_attempts=6 +health_attempt=1 + +while [ $health_attempt -le $max_health_attempts ]; do + if curl -f http://localhost:3000/api/health &> /dev/null; then + log "✅ Application démarrée avec succès" + break + fi + + if [ $health_attempt -eq $max_health_attempts ]; then + log "⚠️ Application non accessible après $max_health_attempts tentatives" + log "📊 Vérification des logs:" + docker-compose logs --tail=20 logiflow + break + fi + + log "⏳ Test santé $health_attempt/$max_health_attempts - attente 10 secondes..." + sleep 10 + ((health_attempt++)) +done + +# Vérification de la migration +log "🔍 Vérification de la migration DLC..." +if docker-compose exec -T logiflow-db psql -U logiflow_admin -d logiflow_db -tAc "SELECT EXISTS (SELECT FROM information_schema.columns WHERE table_name='dlc_products' AND column_name='stock_epuise');" | grep -q "t"; then + log "✅ Colonnes stock épuisé présentes" +else + log "❌ Colonnes stock épuisé manquantes" + exit 1 +fi + +# Test des nouvelles routes API +log "🧪 Test des nouvelles routes API..." +echo "Testez manuellement les routes suivantes :" +echo "- PUT /api/dlc-products/:id/stock-epuise" +echo "- PUT /api/dlc-products/:id/restore-stock" + +# Désactivation du mode maintenance +log "🟢 Désactivation du mode maintenance..." +rm -f maintenance.html + +# Résumé final +log "🎉 DÉPLOIEMENT TERMINÉ AVEC SUCCÈS!" +echo "" +echo "==================================================" +echo "📊 RÉSUMÉ DU DÉPLOIEMENT" +echo "==================================================" +echo "✅ Application reconstruite et redémarrée" +echo "✅ Base de données migrée avec les nouveaux champs" +echo "✅ Tests de santé réussis" +echo "" +echo "🔍 VÉRIFICATIONS POST-DÉPLOIEMENT:" +echo "1. Interface DLC accessible" +echo "2. Boutons PackageX et RotateCcw présents" +echo "3. Fonctionnalité stock épuisé opérationnelle" +echo "4. Permissions admin/directeur/manager pour restauration" +echo "" +echo "📱 MONITORING:" +echo "docker-compose logs -f logiflow" +echo "" +echo "🐛 EN CAS DE PROBLÈME:" +echo "./scripts/verify-dlc-migration.sh" +echo "docker-compose restart" +echo "" +if [ -f "${backup_file}" ]; then + echo "💾 SAUVEGARDE DISPONIBLE: ${backup_file}" +fi +echo "==================================================" \ No newline at end of file diff --git a/scripts/verify-dlc-migration.sh b/scripts/verify-dlc-migration.sh new file mode 100755 index 0000000..db6eaef --- /dev/null +++ b/scripts/verify-dlc-migration.sh @@ -0,0 +1,111 @@ +#!/bin/bash + +# Script de vérification de la migration DLC stock épuisé +# Auteur: Système de gestion automatisé +# Date: 2025-08-18 + +set -e + +echo "🔍 [VERIFY] Vérification de la migration DLC stock épuisé..." + +# Charger la configuration de la base de données +if [ -f ".env.production" ]; then + source .env.production + echo "✅ [VERIFY] Configuration production chargée" +else + echo "⚠️ [VERIFY] Fichier .env.production non trouvé, utilisation des variables d'environnement" +fi + +# Construire l'URL de connexion si nécessaire +if [ -z "$DATABASE_URL" ]; then + if [ -n "$POSTGRES_USER" ] && [ -n "$POSTGRES_PASSWORD" ] && [ -n "$POSTGRES_DB" ]; then + export DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@logiflow-db:5432/${POSTGRES_DB}" + echo "🔧 [VERIFY] URL de base de données construite à partir des variables" + else + echo "❌ [VERIFY] Impossible de construire l'URL de base de données" + exit 1 + fi +fi + +echo "🔗 [VERIFY] Test de connexion à la base de données..." +if ! psql "$DATABASE_URL" -c "SELECT 1" > /dev/null 2>&1; then + echo "❌ [VERIFY] Connexion à la base de données échouée" + exit 1 +fi +echo "✅ [VERIFY] Connexion à la base de données réussie" + +# Vérifier l'existence de la table dlc_products +echo "🔍 [VERIFY] Vérification de l'existence de la table dlc_products..." +if ! psql "$DATABASE_URL" -tAc "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema='public' AND table_name='dlc_products');" | grep -q "t"; then + echo "❌ [VERIFY] Table dlc_products n'existe pas" + exit 1 +fi +echo "✅ [VERIFY] Table dlc_products existe" + +# Vérifier les colonnes stock épuisé +echo "🔍 [VERIFY] Vérification des colonnes stock épuisé..." +missing_columns=() + +for column in "stock_epuise" "stock_epuise_by" "stock_epuise_at"; do + if psql "$DATABASE_URL" -tAc "SELECT EXISTS (SELECT FROM information_schema.columns WHERE table_name='dlc_products' AND column_name='$column');" | grep -q "f"; then + missing_columns+=("$column") + fi +done + +if [ ${#missing_columns[@]} -gt 0 ]; then + echo "❌ [VERIFY] Colonnes manquantes: ${missing_columns[*]}" + echo "🔧 [VERIFY] La migration doit être exécutée" + exit 1 +else + echo "✅ [VERIFY] Toutes les colonnes stock épuisé sont présentes" +fi + +# Vérifier les types de données +echo "🔍 [VERIFY] Vérification des types de données..." +psql "$DATABASE_URL" -c " +SELECT + column_name, + data_type, + is_nullable, + column_default +FROM information_schema.columns +WHERE table_name = 'dlc_products' +AND column_name IN ('stock_epuise', 'stock_epuise_by', 'stock_epuise_at') +ORDER BY column_name; +" + +# Vérifier l'index +echo "🔍 [VERIFY] Vérification de l'index stock épuisé..." +if psql "$DATABASE_URL" -tAc "SELECT EXISTS (SELECT FROM pg_indexes WHERE tablename='dlc_products' AND indexname='idx_dlc_products_stock_epuise');" | grep -q "t"; then + echo "✅ [VERIFY] Index idx_dlc_products_stock_epuise existe" +else + echo "⚠️ [VERIFY] Index idx_dlc_products_stock_epuise manquant" +fi + +# Test d'une requête simple +echo "🔍 [VERIFY] Test d'une requête avec les nouveaux champs..." +if psql "$DATABASE_URL" -c "SELECT stock_epuise, stock_epuise_by, stock_epuise_at FROM dlc_products LIMIT 1;" > /dev/null 2>&1; then + echo "✅ [VERIFY] Requête avec les nouveaux champs réussie" +else + echo "❌ [VERIFY] Erreur lors de la requête avec les nouveaux champs" + exit 1 +fi + +# Compter les produits par statut de stock +echo "🔍 [VERIFY] Statistiques des produits par statut de stock..." +psql "$DATABASE_URL" -c " +SELECT + CASE + WHEN stock_epuise = true THEN 'stock_epuise' + WHEN stock_epuise = false THEN 'stock_disponible' + ELSE 'indetermine' + END as statut_stock, + COUNT(*) as nombre_produits +FROM dlc_products +GROUP BY stock_epuise +ORDER BY stock_epuise; +" + +echo "" +echo "✅ [VERIFY] Vérification de la migration DLC terminée avec succès!" +echo "🚀 [VERIFY] L'application peut maintenant utiliser les fonctionnalités stock épuisé" \ No newline at end of file