import type { Express } from "express"; import { createServer, type Server } from "http"; import { storage } from "./storage.production"; import { setupLocalAuth, requireAuth } from "./localAuth.production"; // Alias pour compatibilité const isAuthenticated = requireAuth; const setupAuth = setupLocalAuth; import { insertGroupSchema, insertSupplierSchema, insertOrderSchema, insertDeliverySchema, insertUserGroupSchema, insertPublicitySchema, insertCustomerOrderSchema, insertRoleSchema, insertPermissionSchema, insertUserRoleSchema, insertDlcProductSchema, insertDlcProductFrontendSchema, insertTaskSchema } from "../shared/schema"; import { z } from "zod"; export async function registerRoutes(app: Express): Promise { // Health check endpoint for Docker app.get('/api/health', (req, res) => { res.status(200).json({ status: 'healthy', timestamp: new Date().toISOString(), environment: process.env.NODE_ENV || 'production', database: 'connected' }); }); // Auth middleware setupAuth(app); // All routes from the original routes.ts file // Groups routes app.get('/api/groups', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); if (!user) { return res.status(404).json({ message: "User not found" }); } if (user.role === 'admin') { const groups = await storage.getGroups(); res.json(groups); } else { const userGroups = user.userGroups.map((ug: any) => ug.group); res.json(userGroups); } } catch (error) { console.error("Error fetching groups:", error); res.status(500).json({ message: "Failed to fetch groups" }); } }); app.post('/api/groups', isAuthenticated, async (req: any, res) => { try { console.log('🏪 POST /api/groups - Raw request received'); console.log('📨 Request headers:', { 'content-type': req.headers['content-type'], 'content-length': req.headers['content-length'], 'user-agent': req.headers['user-agent']?.substring(0, 50) }); console.log('📋 Request body type:', typeof req.body); console.log('📋 Request body content:', JSON.stringify(req.body, null, 2)); console.log('📋 Request body keys:', Object.keys(req.body || {})); const userId = req.user.claims ? req.user.claims.sub : req.user.id; console.log('🔐 User requesting group creation:', userId); const user = await storage.getUser(userId); if (!user || (user.role !== 'admin' && user.role !== 'manager')) { console.log('❌ Insufficient permissions for user:', { userId, userRole: user?.role }); return res.status(403).json({ message: "Insufficient permissions" }); } console.log('✅ User has permission to create group:', user.role); const result = insertGroupSchema.safeParse(req.body); if (!result.success) { console.log('❌ Group validation failed:', result.error.errors); return res.status(400).json({ message: "Invalid input", errors: result.error.errors }); } console.log('✅ Group data validation passed:', result.data); const group = await storage.createGroup(result.data); console.log('✅ Group creation successful:', group); res.status(201).json(group); } catch (error) { console.error("❌ Error creating group:", error); console.error("📊 Full error details:", { message: error.message, stack: error.stack, code: error.code, detail: error.detail }); res.status(500).json({ message: "Failed to create group", error: error.message, details: error.detail || "Database error occurred" }); } }); app.put('/api/groups/:id', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || (user.role !== 'admin' && user.role !== 'manager')) { return res.status(403).json({ message: "Insufficient permissions" }); } const id = parseInt(req.params.id); const result = insertGroupSchema.partial().safeParse(req.body); if (!result.success) { return res.status(400).json({ message: "Invalid input", errors: result.error.errors }); } const group = await storage.updateGroup(id, result.data); res.json(group); } catch (error) { console.error("Error updating group:", error); res.status(500).json({ message: "Failed to update group" }); } }); app.delete('/api/groups/:id', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || (user.role !== 'admin' && user.role !== 'manager')) { return res.status(403).json({ message: "Insufficient permissions" }); } const id = parseInt(req.params.id); await storage.deleteGroup(id); res.status(204).send(); } catch (error) { console.error("Error deleting group:", error); res.status(500).json({ message: "Failed to delete group" }); } }); // Suppliers routes app.get('/api/suppliers', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || (user.role !== 'admin' && user.role !== 'manager')) { return res.status(403).json({ message: "Insufficient permissions" }); } // Check if DLC filter is requested const dlcOnly = req.query.dlc === 'true'; const suppliers = await storage.getSuppliers(dlcOnly); res.json(suppliers); } catch (error) { console.error("Error fetching suppliers:", error); res.status(500).json({ message: "Failed to fetch suppliers" }); } }); app.post('/api/suppliers', isAuthenticated, async (req: any, res) => { try { console.log('🚚 POST /api/suppliers - Raw request received'); console.log('📨 Request headers:', { 'content-type': req.headers['content-type'], 'content-length': req.headers['content-length'], 'user-agent': req.headers['user-agent']?.substring(0, 50) }); console.log('📋 Request body type:', typeof req.body); console.log('📋 Request body content:', JSON.stringify(req.body, null, 2)); console.log('📋 Request body keys:', Object.keys(req.body || {})); const userId = req.user.claims ? req.user.claims.sub : req.user.id; console.log('🔐 User requesting supplier creation:', userId); const user = await storage.getUser(userId); if (!user || (user.role !== 'admin' && user.role !== 'manager')) { console.log('❌ Insufficient permissions for user:', { userId, userRole: user?.role }); return res.status(403).json({ message: "Insufficient permissions" }); } console.log('✅ User has permission to create supplier:', user.role); const result = insertSupplierSchema.safeParse(req.body); if (!result.success) { console.log('❌ Supplier validation failed:', result.error.errors); return res.status(400).json({ message: "Invalid input", errors: result.error.errors }); } console.log('✅ Supplier data validation passed:', result.data); const supplier = await storage.createSupplier(result.data); console.log('✅ Supplier creation successful:', supplier); res.status(201).json(supplier); } catch (error) { console.error("❌ Error creating supplier:", error); console.error("📊 Full error details:", { message: error.message, stack: error.stack, code: error.code, detail: error.detail }); res.status(500).json({ message: "Failed to create supplier", error: error.message, details: error.detail || "Database error occurred" }); } }); app.put('/api/suppliers/:id', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || (user.role !== 'admin' && user.role !== 'manager')) { return res.status(403).json({ message: "Insufficient permissions" }); } const id = parseInt(req.params.id); const result = insertSupplierSchema.partial().safeParse(req.body); if (!result.success) { return res.status(400).json({ message: "Invalid input", errors: result.error.errors }); } const supplier = await storage.updateSupplier(id, result.data); res.json(supplier); } catch (error) { console.error("Error updating supplier:", error); res.status(500).json({ message: "Failed to update supplier" }); } }); app.delete('/api/suppliers/:id', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || (user.role !== 'admin' && user.role !== 'manager')) { return res.status(403).json({ message: "Insufficient permissions" }); } const id = parseInt(req.params.id); await storage.deleteSupplier(id); res.status(204).send(); } catch (error) { console.error("Error deleting supplier:", error); res.status(500).json({ message: "Failed to delete supplier" }); } }); // Orders routes app.get('/api/orders', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); if (!user) { return res.status(404).json({ message: "User not found" }); } const { startDate, endDate, storeId } = req.query; console.log('📦 Orders API called with:', { startDate, endDate, storeId, storeIdType: typeof storeId, userRole: user.role, fullQuery: req.query }); let groupIds: number[] | undefined; if (user.role === 'admin') { if (storeId && storeId !== 'undefined' && storeId !== 'null') { groupIds = [parseInt(storeId as string)]; console.log('📦 Admin filtering with groupIds:', groupIds, 'from storeId:', storeId); } else { groupIds = undefined; console.log('📦 Admin filtering with groupIds: undefined (all stores)'); } } else { groupIds = user.userGroups.map((ug: any) => ug.groupId); console.log('📦 Non-admin filtering with groupIds:', groupIds); } let orders; if (startDate && endDate) { console.log('📦 Fetching orders by date range:', startDate, 'to', endDate); orders = await storage.getOrdersByDateRange(startDate as string, endDate as string, groupIds); } else { console.log('📦 Fetching all orders with groupIds:', groupIds); orders = await storage.getOrders(groupIds); } console.log('📦 Orders returned:', orders.length, 'items'); res.json(orders); } catch (error) { console.error("Error fetching orders:", error); res.status(500).json({ message: "Failed to fetch orders" }); } }); app.post('/api/orders', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const orderData = { ...req.body, createdBy: userId, }; const result = insertOrderSchema.safeParse(orderData); if (!result.success) { return res.status(400).json({ message: "Invalid input", errors: result.error.errors }); } const order = await storage.createOrder(result.data); res.status(201).json(order); } catch (error) { console.error("Error creating order:", error); res.status(500).json({ message: "Failed to create order" }); } }); app.put('/api/orders/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const result = insertOrderSchema.partial().safeParse(req.body); if (!result.success) { return res.status(400).json({ message: "Invalid input", errors: result.error.errors }); } const order = await storage.updateOrder(id, result.data); res.json(order); } catch (error) { console.error("Error updating order:", error); res.status(500).json({ message: "Failed to update order" }); } }); app.delete('/api/orders/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); console.log('🗑️ Production - Deleting order:', id); await storage.deleteOrder(id); console.log('✅ Production - Order deleted successfully:', id); res.status(204).send(); } catch (error) { console.error("❌ Production - Error deleting order:", error); res.status(500).json({ message: "Failed to delete order" }); } }); // Deliveries routes app.get('/api/deliveries', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); if (!user) { return res.status(404).json({ message: "User not found" }); } const { startDate, endDate, storeId, withBL } = req.query; console.log('🚚 Deliveries API called with:', { startDate, endDate, storeId, withBL, userRole: user.role }); let groupIds: number[] | undefined; if (user.role === 'admin') { if (storeId && storeId !== 'undefined' && storeId !== 'null') { groupIds = [parseInt(storeId as string)]; console.log('🚚 Admin filtering deliveries with groupIds:', groupIds); } else { groupIds = undefined; console.log('🚚 Admin filtering deliveries with groupIds: undefined (all stores)'); } } else { groupIds = user.userGroups.map((ug: any) => ug.groupId); console.log('🚚 Non-admin filtering deliveries with groupIds:', groupIds); } let deliveries; if (startDate && endDate) { console.log('🚚 Fetching deliveries by date range:', startDate, 'to', endDate); deliveries = await storage.getDeliveriesByDateRange(startDate as string, endDate as string, groupIds); } else { console.log('🚚 Fetching all deliveries with groupIds:', groupIds); deliveries = await storage.getDeliveries(groupIds); } console.log('🚚 Deliveries returned:', deliveries.length, 'items'); res.json(deliveries); } catch (error) { console.error("Error fetching deliveries:", error); res.status(500).json({ message: "Failed to fetch deliveries" }); } }); app.post('/api/deliveries', isAuthenticated, async (req: any, res) => { try { console.log('🚚 PRODUCTION /api/deliveries POST - Raw body:', JSON.stringify(req.body, null, 2)); const userId = req.user.claims ? req.user.claims.sub : req.user.id; console.log('🚚 PRODUCTION - User ID:', userId); const deliveryData = { ...req.body, createdBy: userId, }; console.log('🚚 PRODUCTION - Full delivery data before validation:', JSON.stringify(deliveryData, null, 2)); const result = insertDeliverySchema.safeParse(deliveryData); if (!result.success) { console.error('❌ PRODUCTION - Validation failed:', result.error.errors); return res.status(400).json({ message: "Invalid input", errors: result.error.errors }); } console.log('✅ PRODUCTION - Validation passed. Data to insert:', JSON.stringify(result.data, null, 2)); const delivery = await storage.createDelivery(result.data); console.log('✅ PRODUCTION - Delivery created successfully:', { id: delivery.id, status: delivery.status }); res.status(201).json(delivery); } catch (error) { console.error("❌ PRODUCTION Error creating delivery:", error); console.error("❌ PRODUCTION Error details:", { message: error.message, code: error.code, constraint: error.constraint, detail: error.detail }); res.status(500).json({ message: "Failed to create delivery" }); } }); // Route spécifique AVANT la route générale pour éviter l'interception app.post('/api/deliveries/:id/validate', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const { blNumber, blAmount } = req.body; console.log('🔍 POST /api/deliveries/:id/validate - Request:', { id, blNumber, blAmount }); if (isNaN(id)) { return res.status(400).json({ message: "ID de livraison invalide" }); } // Validation des données BL if (blNumber && typeof blNumber !== 'string') { return res.status(400).json({ message: "Le numéro BL doit être une chaîne de caractères" }); } if (blAmount !== undefined && (isNaN(blAmount) || blAmount < 0)) { return res.status(400).json({ message: "Le montant BL doit être un nombre positif" }); } await storage.validateDelivery(id, { blNumber, blAmount }); console.log('✅ Delivery validated successfully:', { id, blNumber, blAmount }); res.json({ message: "Livraison validée avec succès" }); } catch (error) { console.error("❌ Error validating delivery:", error); if (error.message.includes('column') && error.message.includes('does not exist')) { console.error('💾 Database schema issue:', error.message); return res.status(500).json({ message: "Erreur de structure de base de données. Contactez l'administrateur." }); } if (error.message.includes('constraint') || error.message.includes('check')) { return res.status(400).json({ message: "Données invalides pour la validation" }); } res.status(500).json({ message: "Erreur lors de la validation de la livraison" }); } }); app.put('/api/deliveries/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const result = insertDeliverySchema.partial().safeParse(req.body); if (!result.success) { return res.status(400).json({ message: "Invalid input", errors: result.error.errors }); } const delivery = await storage.updateDelivery(id, result.data); res.json(delivery); } catch (error) { console.error("Error updating delivery:", error); res.status(500).json({ message: "Failed to update delivery" }); } }); app.delete('/api/deliveries/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); await storage.deleteDelivery(id); res.status(204).send(); } catch (error) { console.error("Error deleting delivery:", error); res.status(500).json({ message: "Failed to delete delivery" }); } }); // User management routes app.get('/api/users', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); // ✅ CORRECTION: Permettre aux admins ET managers de voir les utilisateurs (nécessaire pour la gestion des rôles) if (!user || (user.role !== 'admin' && user.role !== 'manager')) { return res.status(403).json({ message: "Access denied" }); } // 🔧 CORRECTION CRITIQUE: Utiliser getUsersWithRolesAndGroups() au lieu de getUsers() pour l'affichage des rôles const usersWithRoles = await storage.getUsersWithRolesAndGroups(); const safeUsers = Array.isArray(usersWithRoles) ? usersWithRoles : []; console.log('🔐 API /api/users - Returning:', { isArray: Array.isArray(safeUsers), length: safeUsers.length }); console.log('👥 Users API response:', { count: safeUsers.length, requestingUser: user.role }); res.json(safeUsers); } catch (error) { console.error("Error fetching users:", error); // En cas d'erreur, retourner un array vide pour éviter React Error #310 res.status(500).json([]); } }); app.post('/api/users', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Access denied" }); } const newUser = await storage.createUser(req.body); res.status(201).json(newUser); } catch (error) { console.error("Error creating user:", error); res.status(500).json({ message: "Failed to create user" }); } }); app.put('/api/users/:id', isAuthenticated, async (req: any, res) => { try { const currentUserId = req.user.claims ? req.user.claims.sub : req.user.id; const currentUser = await storage.getUser(currentUserId); if (!currentUser || currentUser.role !== 'admin') { return res.status(403).json({ message: "Access denied" }); } const id = req.params.id; // Validation des champs requis const userData = req.body; console.log('📝 PUT /api/users/:id - Request body:', userData); console.log('📝 User ID from params:', id); // Vérifier que l'utilisateur existe avant modification const existingUser = await storage.getUser(id); if (!existingUser) { console.log('❌ User not found with ID:', id); return res.status(404).json({ message: `Utilisateur avec l'ID ${id} non trouvé` }); } console.log('✅ Found user to update:', existingUser.username); // Nettoyer les données - supprimer les champs vides ou undefined const cleanedUserData = {}; for (const [key, value] of Object.entries(userData)) { if (value !== undefined && value !== null && (typeof value !== 'string' || value.trim() !== '')) { cleanedUserData[key] = typeof value === 'string' ? value.trim() : value; } } console.log('📝 Cleaned user data:', cleanedUserData); if (Object.keys(cleanedUserData).length === 0) { console.log('⚠️ No valid data to update'); return res.status(400).json({ message: "Aucune donnée valide à mettre à jour" }); } // Validation des champs obligatoires seulement si ils sont fournis et non vides if (cleanedUserData.firstName && !cleanedUserData.firstName.trim()) { return res.status(400).json({ message: "Le prénom ne peut pas être vide" }); } if (cleanedUserData.lastName && !cleanedUserData.lastName.trim()) { return res.status(400).json({ message: "Le nom ne peut pas être vide" }); } if (cleanedUserData.email) { if (!cleanedUserData.email.includes('@')) { return res.status(400).json({ message: "L'email doit être valide" }); } } if (cleanedUserData.password && cleanedUserData.password.length < 6) { return res.status(400).json({ message: "Le mot de passe doit contenir au moins 6 caractères" }); } const updatedUser = await storage.updateUser(id, cleanedUserData); console.log('✅ User updated successfully:', { id, updatedFields: Object.keys(cleanedUserData) }); res.json(updatedUser); } catch (error) { console.error("❌ Error updating user:", error); // Gestion des erreurs spécifiques if (error.message.includes('email') && error.message.includes('unique')) { return res.status(400).json({ message: "Cet email est déjà utilisé par un autre utilisateur" }); } if (error.message.includes('username') && error.message.includes('unique')) { return res.status(400).json({ message: "Ce nom d'utilisateur est déjà pris" }); } if (error.message.includes('ne peut pas être vide') || error.message.includes('doit être valide') || error.message.includes('Aucun champ') || error.message.includes('non trouvé')) { return res.status(400).json({ message: error.message }); } res.status(500).json({ message: "Erreur lors de la mise à jour de l'utilisateur" }); } }); app.delete('/api/users/:id', isAuthenticated, async (req: any, res) => { try { const currentUserId = req.user.claims ? req.user.claims.sub : req.user.id; const currentUser = await storage.getUser(currentUserId); if (!currentUser || currentUser.role !== 'admin') { return res.status(403).json({ message: "Access denied" }); } const id = req.params.id; await storage.deleteUser(id); res.status(204).send(); } catch (error) { console.error("Error deleting user:", error); res.status(500).json({ message: "Failed to delete user" }); } }); // User groups routes app.get('/api/users/:userId/groups', isAuthenticated, async (req: any, res) => { try { const userId = req.params.userId; const userGroups = await storage.getUserGroups(userId); res.json(userGroups); } catch (error) { console.error("Error fetching user groups:", error); res.status(500).json({ message: "Failed to fetch user groups" }); } }); // Route supprimée car dupliquée - utilisation de la route unique en bas du fichier // Statistics routes app.get('/api/stats/monthly', isAuthenticated, async (req: any, res) => { try { const year = parseInt(req.query.year as string) || new Date().getFullYear(); const month = parseInt(req.query.month as string) || new Date().getMonth() + 1; const storeId = req.query.storeId as string; const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); if (!user) { return res.status(404).json({ message: "User not found" }); } let groupIds: number[] | undefined; if (user.role === 'admin') { groupIds = storeId ? [parseInt(storeId)] : undefined; } else { groupIds = user.userGroups.map((ug: any) => ug.groupId); } const stats = await storage.getMonthlyStats(year, month, groupIds); res.json(stats); } catch (error) { console.error("Error fetching monthly stats:", error); res.status(500).json({ message: "Failed to fetch monthly stats" }); } }); // Publicities routes app.get('/api/publicities', isAuthenticated, async (req: any, res) => { try { const year = req.query.year ? parseInt(req.query.year as string) : undefined; const publicities = await storage.getPublicities(year); res.json(publicities); } catch (error) { console.error("Error fetching publicities:", error); res.status(500).json({ message: "Failed to fetch publicities" }); } }); app.post('/api/publicities', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user) { return res.status(404).json({ message: "User not found" }); } if (user.role !== 'admin') { return res.status(403).json({ message: "Access denied - Admin role required", userRole: user.role }); } const { participatingGroups, ...publicityData } = req.body; // Create publicity const publicity = await storage.createPublicity({ ...publicityData, createdBy: userId, }); // Set participations if provided if (participatingGroups && participatingGroups.length > 0) { await storage.setPublicityParticipations(publicity.id, participatingGroups); } // Get the complete publicity with relations const completePublicity = await storage.getPublicity(publicity.id); res.status(201).json(completePublicity); } catch (error) { console.error("Error creating publicity:", error); res.status(500).json({ message: "Failed to create publicity", error: error.message }); } }); app.put('/api/publicities/:id', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Access denied" }); } const id = parseInt(req.params.id); const { participatingGroups, ...publicityData } = req.body; // Update publicity const updatedPublicity = await storage.updatePublicity(id, publicityData); // Update participations if (participatingGroups !== undefined) { await storage.setPublicityParticipations(id, participatingGroups); } // Get the complete publicity with relations const completePublicity = await storage.getPublicity(id); res.json(completePublicity); } catch (error) { console.error("Error updating publicity:", error); res.status(500).json({ message: "Failed to update publicity" }); } }); app.delete('/api/publicities/:id', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Access denied" }); } const id = parseInt(req.params.id); await storage.deletePublicity(id); res.status(204).send(); } catch (error) { console.error("Error deleting publicity:", error); res.status(500).json({ message: "Failed to delete publicity" }); } }); // Customer Orders routes app.get('/api/customer-orders', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); if (!user) { return res.status(404).json({ message: "User not found" }); } const { storeId } = req.query; // Determine which groups to show let groupIds; if (user.role === 'admin' && storeId) { // Admin filtering by specific store groupIds = [parseInt(storeId.toString())]; console.log("Customer orders - Admin filtering by store:", { storeId, groupIds }); } else if (user.role === 'admin') { // Admin viewing all stores - get all groups const allGroups = await storage.getGroups(); groupIds = allGroups.map((g: any) => g.id); console.log("Customer orders - Admin viewing all stores:", { groupCount: groupIds.length }); } else { // Non-admin users see only their assigned stores groupIds = user.userGroups.map((ug: any) => ug.groupId); console.log("Customer orders - User assigned stores:", { groupIds }); } const customerOrders = await storage.getCustomerOrders(groupIds); console.log("Customer orders returned from storage:", customerOrders?.length || 0, "items"); res.json(customerOrders || []); } catch (error) { console.error("Error fetching customer orders:", error); res.status(500).json({ message: "Failed to fetch customer orders" }); } }); app.post('/api/customer-orders', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const customerOrder = await storage.createCustomerOrder({ ...req.body, createdBy: userId, }); res.status(201).json(customerOrder); } catch (error) { console.error("Error creating customer order:", error); res.status(500).json({ message: "Failed to create customer order" }); } }); app.put('/api/customer-orders/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const customerOrder = await storage.updateCustomerOrder(id, req.body); res.json(customerOrder); } catch (error) { console.error("Error updating customer order:", error); res.status(500).json({ message: "Failed to update customer order" }); } }); app.delete('/api/customer-orders/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); await storage.deleteCustomerOrder(id); res.status(204).send(); } catch (error) { console.error("Error deleting customer order:", error); res.status(500).json({ message: "Failed to delete customer order" }); } }); // NocoDB Configuration routes app.get('/api/nocodb-config', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || user.role !== 'admin') { return res.status(403).json({ message: 'Accès refusé. Seuls les administrateurs peuvent gérer les configurations NocoDB.' }); } const configs = await storage.getNocodbConfigs(); console.log('📊 NocoDB configs API:', { count: configs ? configs.length : 0, configs }); // Assurer que la réponse est toujours un array comme en développement const safeConfigs = Array.isArray(configs) ? configs : []; res.json(safeConfigs); } catch (error) { console.error('Error fetching NocoDB configs:', error); res.status(500).json({ message: 'Erreur lors de la récupération des configurations' }); } }); app.post('/api/nocodb-config', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || user.role !== 'admin') { return res.status(403).json({ message: 'Accès refusé. Seuls les administrateurs peuvent gérer les configurations NocoDB.' }); } const config = await storage.createNocodbConfig({ ...req.body, createdBy: userId, }); res.status(201).json(config); } catch (error) { console.error('Error creating NocoDB config:', error); res.status(500).json({ message: 'Erreur lors de la création de la configuration' }); } }); app.put('/api/nocodb-config/:id', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || user.role !== 'admin') { return res.status(403).json({ message: 'Accès refusé. Seuls les administrateurs peuvent gérer les configurations NocoDB.' }); } const id = parseInt(req.params.id); const config = await storage.updateNocodbConfig(id, req.body); res.json(config); } catch (error) { console.error('Error updating NocoDB config:', error); res.status(500).json({ message: 'Erreur lors de la mise à jour de la configuration' }); } }); app.delete('/api/nocodb-config/:id', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUser(userId); if (!user || user.role !== 'admin') { return res.status(403).json({ message: 'Accès refusé. Seuls les administrateurs peuvent gérer les configurations NocoDB.' }); } const id = parseInt(req.params.id); await storage.deleteNocodbConfig(id); res.status(204).send(); } catch (error) { console.error('Error deleting NocoDB config:', error); res.status(500).json({ message: 'Erreur lors de la suppression de la configuration' }); } }); // Invoice verification routes (simplified for production) app.post('/api/verify-invoice', isAuthenticated, async (req: any, res) => { try { const { groupId, invoiceReference } = req.body; if (!groupId || !invoiceReference) { return res.status(400).json({ message: "groupId and invoiceReference are required" }); } // Simplified verification - always return false for production res.json({ exists: false }); } catch (error) { console.error("Error verifying invoice:", error); res.status(500).json({ message: "Failed to verify invoice" }); } }); app.post('/api/verify-invoices', isAuthenticated, async (req: any, res) => { try { const { invoiceReferences } = req.body; if (!Array.isArray(invoiceReferences)) { return res.status(400).json({ message: "invoiceReferences must be an array" }); } // Simplified verification - always return false for production const results: any = {}; invoiceReferences.forEach((ref: any) => { results[ref.deliveryId] = { exists: false }; }); res.json(results); } catch (error) { console.error("Error verifying invoices:", error); res.status(500).json({ message: "Failed to verify invoices" }); } }); // ===== ROLE MANAGEMENT ROUTES ===== // Roles routes app.get('/api/roles', isAuthenticated, async (req: any, res) => { try { // ✅ CORRECTION: Permettre à tous les utilisateurs authentifiés de lire les rôles (nécessaire pour l'affichage des couleurs) const roles = await storage.getRoles(); console.log('🎨 Roles API response:', { count: roles.length, firstRole: roles[0] }); res.json(Array.isArray(roles) ? roles : []); } catch (error) { console.error("Error fetching roles:", error); res.status(500).json({ message: "Failed to fetch roles" }); } }); app.get('/api/roles/:id', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const id = parseInt(req.params.id); const role = await storage.getRoleWithPermissions(id); if (!role) { return res.status(404).json({ message: "Role not found" }); } res.json(role); } catch (error) { console.error("Error fetching role:", error); res.status(500).json({ message: "Failed to fetch role" }); } }); app.post('/api/roles', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const result = insertRoleSchema.safeParse(req.body); if (!result.success) { return res.status(400).json({ message: "Invalid input", errors: result.error.errors }); } const role = await storage.createRole(result.data); res.status(201).json(role); } catch (error) { console.error("Error creating role:", error); res.status(500).json({ message: "Failed to create role" }); } }); app.put('/api/roles/:id', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const id = parseInt(req.params.id); const result = insertRoleSchema.partial().safeParse(req.body); if (!result.success) { return res.status(400).json({ message: "Invalid input", errors: result.error.errors }); } const role = await storage.updateRole(id, result.data); res.json(role); } catch (error) { console.error("Error updating role:", error); res.status(500).json({ message: "Failed to update role" }); } }); app.delete('/api/roles/:id', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const id = parseInt(req.params.id); await storage.deleteRole(id); res.status(204).send(); } catch (error) { console.error("Error deleting role:", error); res.status(500).json({ message: "Failed to delete role" }); } }); app.get('/api/permissions', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; console.log("🔍 PRODUCTION Permissions API - User ID:", userId); const user = await storage.getUserWithGroups(userId); console.log("👤 PRODUCTION Permissions API - User found:", user ? user.role : 'NOT FOUND'); if (!user || user.role !== 'admin') { console.log("❌ PRODUCTION Permissions API - Access denied, user role:", user?.role); return res.status(403).json({ message: "Accès refusé - droits administrateur requis" }); } console.log("🔍 PRODUCTION Fetching all permissions..."); const permissions = await storage.getPermissions(); console.log("📝 PRODUCTION Permissions fetched:", permissions.length, "items"); console.log("🏷️ PRODUCTION Categories found:", [...new Set(permissions.map(p => p.category))]); // 🎯 VÉRIFICATION SPÉCIFIQUE PERMISSIONS TÂCHES const taskPermissions = permissions.filter(p => p.category === 'gestion_taches'); console.log("📋 PRODUCTION Task permissions found:", taskPermissions.length); if (taskPermissions.length > 0) { console.log("📋 PRODUCTION Task permissions details:"); taskPermissions.forEach(p => { console.log(` - ID: ${p.id}, Name: ${p.name}, DisplayName: "${p.displayName}", Category: ${p.category}`); }); } else { console.log('❌ PRODUCTION NO TASK PERMISSIONS FOUND - This explains the problem!'); } res.json(Array.isArray(permissions) ? permissions : []); } catch (error) { console.error("Error fetching permissions:", error); res.status(500).json({ message: "Failed to fetch permissions" }); } }); // Set permissions for a role app.post('/api/roles/:id/permissions', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { console.log("❌ ROLE PERMISSIONS UPDATE - Access denied, user role:", user?.role); return res.status(403).json({ message: "Accès refusé - droits administrateur requis" }); } const roleId = parseInt(req.params.id); const { permissionIds } = req.body; console.log("🔄 PRODUCTION Updating role permissions for role ID:", roleId); console.log("📝 PRODUCTION New permission IDs:", permissionIds); if (!Array.isArray(permissionIds)) { console.log("❌ PRODUCTION Invalid permissionIds format:", typeof permissionIds); return res.status(400).json({ message: "permissionIds doit être un tableau" }); } await storage.setRolePermissions(roleId, permissionIds); // Récupérer les nouvelles permissions pour confirmation const updatedPermissions = await storage.getRolePermissions(roleId); console.log("✅ PRODUCTION Role permissions updated successfully:", updatedPermissions.length, "permissions"); res.json({ success: true, message: "Permissions mises à jour avec succès", permissionCount: updatedPermissions.length }); } catch (error) { console.error("❌ PRODUCTION Error updating role permissions:", error); res.status(500).json({ message: "Erreur lors de la mise à jour des permissions" }); } }); // 🚨 ENDPOINT TEMPORAIRE - CORRIGER PERMISSIONS ADMIN MANQUANTES app.post('/api/admin/fix-permissions', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Seul l'admin peut corriger les permissions" }); } console.log('🔧 PRODUCTION: Correction des permissions admin manquantes'); const { pool } = require('./initDatabase.production'); // Récupérer l'ID du rôle admin const adminRoleResult = await pool.query('SELECT id FROM roles WHERE name = $1', ['admin']); if (adminRoleResult.rows.length === 0) { throw new Error('Rôle admin non trouvé'); } const adminRoleId = adminRoleResult.rows[0].id; // Récupérer toutes les permissions const allPermissionsResult = await pool.query('SELECT id, name FROM permissions ORDER BY id'); const allPermissions = allPermissionsResult.rows; // Récupérer les permissions actuelles de l'admin const currentPermissionsResult = await pool.query( 'SELECT permission_id FROM role_permissions WHERE role_id = $1', [adminRoleId] ); const currentPermissionIds = new Set(currentPermissionsResult.rows.map(rp => rp.permission_id)); // Identifier les permissions manquantes const missingPermissions = allPermissions.filter(p => !currentPermissionIds.has(p.id)); console.log(`📊 Admin actuel: ${currentPermissionIds.size} permissions sur ${allPermissions.length} totales`); console.log(`⚠️ Permissions manquantes: ${missingPermissions.length}`); if (missingPermissions.length > 0) { console.log('📋 Ajout des permissions manquantes:'); for (const perm of missingPermissions) { await pool.query(` INSERT INTO role_permissions (role_id, permission_id) VALUES ($1, $2) ON CONFLICT (role_id, permission_id) DO NOTHING `, [adminRoleId, perm.id]); console.log(` ✅ Ajouté: ${perm.name}`); } } // Vérification finale const finalCountResult = await pool.query( 'SELECT COUNT(*) as count FROM role_permissions WHERE role_id = $1', [adminRoleId] ); const finalCount = finalCountResult.rows[0].count; res.json({ message: "Permissions admin corrigées avec succès", before: currentPermissionIds.size, after: parseInt(finalCount), added: missingPermissions.length, total: allPermissions.length }); } catch (error) { console.error('❌ Erreur correction permissions admin:', error); res.status(500).json({ message: "Erreur lors de la correction des permissions" }); } }); // 🚨 ENDPOINT TEMPORAIRE DE DIAGNOSTIC PRODUCTION PERMISSIONS TÂCHES app.get('/api/debug/task-permissions', isAuthenticated, async (req: any, res) => { try { console.log('🔍 PRODUCTION TASK PERMISSIONS DEBUG'); // Vérifier directement en base de données const { pool } = require('./initDatabase.production'); const taskResult = await pool.query(` SELECT id, name, display_name, category, action, resource FROM permissions WHERE category = 'gestion_taches' ORDER BY name `); console.log('📋 Task permissions in DB:', taskResult.rows.length); taskResult.rows.forEach(row => { console.log(` - ID: ${row.id}, Name: ${row.name}, DisplayName: "${row.display_name}", Category: ${row.category}`); }); // Vérifier aussi via le storage const storagePermissions = await storage.getPermissions(); const storageTaskPermissions = storagePermissions.filter(p => p.category === 'gestion_taches'); console.log('📋 Task permissions via storage:', storageTaskPermissions.length); res.json({ database: taskResult.rows, storage: storageTaskPermissions, summary: { database_count: taskResult.rows.length, storage_count: storageTaskPermissions.length, timestamp: new Date().toISOString() } }); } catch (error) { console.error('❌ Task permissions debug error:', error); res.status(500).json({ message: "Debug failed", error: error.message }); } }); // 🚨 ENDPOINT TEMPORAIRE POUR APPLIQUER LES CORRECTIONS SQL app.post('/api/debug/fix-translations', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Seul l'admin peut appliquer les corrections" }); } console.log('🔧 APPLYING SQL FIXES TO PRODUCTION DATABASE'); const { pool } = require('./initDatabase.production'); // Corriger les rôles console.log('📝 Fixing roles...'); await pool.query("UPDATE roles SET display_name = 'Administrateur' WHERE name = 'admin'"); await pool.query("UPDATE roles SET display_name = 'Manager' WHERE name = 'manager'"); await pool.query("UPDATE roles SET display_name = 'Employé' WHERE name = 'employee' OR name = 'employé'"); await pool.query("UPDATE roles SET display_name = 'Directeur' WHERE name = 'directeur'"); // Corriger les permissions principales console.log('📝 Fixing main permissions...'); const permissionUpdates = [ ["UPDATE permissions SET display_name = 'Voir calendrier' WHERE name = 'calendar_read'"], ["UPDATE permissions SET display_name = 'Créer événements' WHERE name = 'calendar_create'"], ["UPDATE permissions SET display_name = 'Modifier calendrier' WHERE name = 'calendar_update'"], ["UPDATE permissions SET display_name = 'Supprimer événements' WHERE name = 'calendar_delete'"], ["UPDATE permissions SET display_name = 'Voir tableau de bord' WHERE name = 'dashboard_read'"], ["UPDATE permissions SET display_name = 'Voir livraisons' WHERE name = 'deliveries_read'"], ["UPDATE permissions SET display_name = 'Créer livraisons' WHERE name = 'deliveries_create'"], ["UPDATE permissions SET display_name = 'Modifier livraisons' WHERE name = 'deliveries_update'"], ["UPDATE permissions SET display_name = 'Supprimer livraisons' WHERE name = 'deliveries_delete'"], ["UPDATE permissions SET display_name = 'Valider livraisons' WHERE name = 'deliveries_validate'"], ["UPDATE permissions SET display_name = 'Voir magasins' WHERE name = 'groups_read'"], ["UPDATE permissions SET display_name = 'Créer magasins' WHERE name = 'groups_create'"], ["UPDATE permissions SET display_name = 'Modifier magasins' WHERE name = 'groups_update'"], ["UPDATE permissions SET display_name = 'Supprimer magasins' WHERE name = 'groups_delete'"], ["UPDATE permissions SET display_name = 'Voir commandes' WHERE name = 'orders_read'"], ["UPDATE permissions SET display_name = 'Créer commandes' WHERE name = 'orders_create'"], ["UPDATE permissions SET display_name = 'Modifier commandes' WHERE name = 'orders_update'"], ["UPDATE permissions SET display_name = 'Supprimer commandes' WHERE name = 'orders_delete'"], ["UPDATE permissions SET display_name = 'Voir publicités' WHERE name = 'publicities_read'"], ["UPDATE permissions SET display_name = 'Créer publicités' WHERE name = 'publicities_create'"], ["UPDATE permissions SET display_name = 'Modifier publicités' WHERE name = 'publicities_update'"], ["UPDATE permissions SET display_name = 'Supprimer publicités' WHERE name = 'publicities_delete'"], ["UPDATE permissions SET display_name = 'Voir fournisseurs' WHERE name = 'suppliers_read'"], ["UPDATE permissions SET display_name = 'Créer fournisseurs' WHERE name = 'suppliers_create'"], ["UPDATE permissions SET display_name = 'Modifier fournisseurs' WHERE name = 'suppliers_update'"], ["UPDATE permissions SET display_name = 'Supprimer fournisseurs' WHERE name = 'suppliers_delete'"], ]; for (const [query] of permissionUpdates) { await pool.query(query); } // Vérifier les résultats const rolesResult = await pool.query("SELECT name, display_name FROM roles ORDER BY name"); const permissionsResult = await pool.query("SELECT name, display_name FROM permissions WHERE name IN ('calendar_read', 'dashboard_read', 'deliveries_read', 'groups_read') ORDER BY name"); console.log('✅ SQL FIXES APPLIED SUCCESSFULLY'); console.log('📊 Updated roles:', rolesResult.rows); console.log('📊 Sample updated permissions:', permissionsResult.rows); res.json({ message: 'Corrections appliquées avec succès', rolesUpdated: rolesResult.rows, permissionsSample: permissionsResult.rows }); } catch (error) { console.error('❌ Error applying SQL fixes:', error); res.status(500).json({ message: "Erreur lors de l'application des corrections", error: error.message }); } }); app.post('/api/permissions', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const permission = await storage.createPermission(req.body); res.status(201).json(permission); } catch (error) { console.error("Error creating permission:", error); res.status(500).json({ message: "Failed to create permission" }); } }); app.put('/api/permissions/:id', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const id = parseInt(req.params.id); const permission = await storage.updatePermission(id, req.body); res.json(permission); } catch (error) { console.error("Error updating permission:", error); res.status(500).json({ message: "Failed to update permission" }); } }); app.delete('/api/permissions/:id', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const id = parseInt(req.params.id); await storage.deletePermission(id); res.status(204).send(); } catch (error) { console.error("Error deleting permission:", error); res.status(500).json({ message: "Failed to delete permission" }); } }); app.get('/api/roles/:id/permissions', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const roleId = parseInt(req.params.id); const rolePermissions = await storage.getRolePermissions(roleId); // 🎯 DEBUG CRITIQUE PRODUCTION: Vérifier pourquoi les permissions tâches ne s'affichent pas console.log('🔍 ROLE PERMISSIONS DEBUG for role ID:', roleId); console.log('📊 Total role permissions found:', rolePermissions?.length || 0); if (Array.isArray(rolePermissions) && rolePermissions.length > 0) { // Afficher toutes les catégories disponibles pour ce rôle const categoriesInRole = [...new Set(rolePermissions.map(rp => rp.permission?.category).filter(Boolean))]; console.log('📂 Categories in role permissions:', categoriesInRole); const taskRolePermissions = rolePermissions.filter(rp => { // Chercher les permissions de la catégorie gestion_taches return rp.permission && rp.permission.category === 'gestion_taches'; }); console.log('🎯 TASK role permissions found:', taskRolePermissions.length); if (taskRolePermissions.length > 0) { console.log('✅ TASK role permissions details:'); taskRolePermissions.forEach(rp => { console.log(` - Permission ID: ${rp.permissionId}, Name: ${rp.permission.name}, DisplayName: "${rp.permission.displayName}"`); }); } else { console.log('❌ NO TASK ROLE PERMISSIONS FOUND - Checking raw data:'); console.log('🔍 First 3 rolePermissions structure:', JSON.stringify(rolePermissions.slice(0, 3), null, 2)); console.log('🔍 Sample permission object:', rolePermissions[0]?.permission ? JSON.stringify(rolePermissions[0].permission, null, 2) : 'No permission object'); } } else { console.log('❌ NO ROLE PERMISSIONS FOUND AT ALL for role:', roleId); } res.json(Array.isArray(rolePermissions) ? rolePermissions : []); } catch (error) { console.error("Error fetching role permissions:", error); res.status(500).json({ message: "Failed to fetch role permissions" }); } }); app.post('/api/roles/:id/permissions', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const roleId = parseInt(req.params.id); const { permissionIds } = req.body; await storage.setRolePermissions(roleId, permissionIds); res.json({ message: "Role permissions updated successfully" }); } catch (error) { console.error("Error updating role permissions:", error); res.status(500).json({ message: "Failed to update role permissions" }); } }); // User-Role association routes (AJOUTÉ POUR PRODUCTION) app.get('/api/users/:userId/roles', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const userId = req.params.userId; const userRoles = await storage.getUserRoles(userId); res.json(userRoles); } catch (error) { console.error("Error fetching user roles:", error); res.status(500).json({ message: "Failed to fetch user roles" }); } }); // POST route for user roles (used by frontend) - ROUTE MANQUANTE AJOUTÉE app.post('/api/users/:userId/roles', isAuthenticated, async (req: any, res) => { try { const currentUser = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!currentUser || currentUser.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const userId = req.params.userId; const { roleIds } = req.body; console.log("🔧 POST User roles API called:", { userId, roleIds, assignedBy: currentUser.id }); if (!Array.isArray(roleIds)) { return res.status(400).json({ message: "roleIds must be an array" }); } await storage.setUserRoles(userId, roleIds, currentUser.id); res.json({ message: "User roles updated successfully" }); } catch (error) { console.error("Error updating user roles:", error); // Gestion d'erreur spécifique avec message plus informatif if (error.message && error.message.includes('does not exist')) { return res.status(404).json({ message: error.message }); } res.status(500).json({ message: "Failed to update user roles" }); } }); app.put('/api/users/:id/roles', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const userId = req.params.id; const { roleIds } = req.body; console.log("🎯 setUserRoles request:", { userId, roleIds, assignedBy: user.id }); await storage.setUserRoles(userId, roleIds, user.id); res.json({ message: "User roles updated successfully" }); } catch (error) { console.error("Error updating user roles:", error); // Gestion d'erreur spécifique avec message plus informatif if (error.message.includes('does not exist')) { return res.status(404).json({ message: error.message }); } res.status(500).json({ message: "Failed to update user roles" }); } }); // User-Group management routes (admin only) - ROUTE CORRIGÉE AVEC DIAGNOSTIC COMPLET app.post('/api/users/:userId/groups', isAuthenticated, async (req: any, res) => { try { console.log('🔍 DIAGNOSTIC: Route /api/users/:userId/groups appelée'); console.log('🔍 DIAGNOSTIC: req.user =', req.user); console.log('🔍 DIAGNOSTIC: req.params =', req.params); console.log('🔍 DIAGNOSTIC: req.body =', req.body); const currentUserId = req.user.claims ? req.user.claims.sub : req.user.id; console.log('🔍 DIAGNOSTIC: currentUserId =', currentUserId); const currentUser = await storage.getUser(currentUserId); console.log('🔍 DIAGNOSTIC: currentUser =', currentUser); if (!currentUser) { console.log('❌ ERREUR: Utilisateur courant non trouvé'); return res.status(401).json({ message: "User not authenticated" }); } if (currentUser.role !== 'admin') { console.log('❌ ERREUR: Permissions insuffisantes, rôle:', currentUser.role); return res.status(403).json({ message: "Admin access required" }); } const userId = req.params.userId; const { groupId } = req.body; console.log('🔍 DIAGNOSTIC: userId =', userId, 'groupId =', groupId); // Vérifier que l'utilisateur cible existe const targetUser = await storage.getUser(userId); if (!targetUser) { console.log('❌ ERREUR: Utilisateur cible non trouvé:', userId); return res.status(404).json({ message: `User not found: ${userId}` }); } console.log('✅ DIAGNOSTIC: Utilisateur cible trouvé:', targetUser.username); // Vérifier que le groupe existe const group = await storage.getGroup(groupId); if (!group) { console.log('❌ ERREUR: Groupe non trouvé:', groupId); return res.status(404).json({ message: `Group not found: ${groupId}` }); } console.log('✅ DIAGNOSTIC: Groupe trouvé:', group.name); // Effectuer l'assignation console.log('🔄 DIAGNOSTIC: Assignation en cours...'); const userGroup = await storage.assignUserToGroup({ userId, groupId }); console.log('✅ SUCCÈS: Assignation réussie:', userGroup); res.json({ success: true, message: `Utilisateur ${targetUser.username} assigné au groupe ${group.name}`, userGroup }); } catch (error) { console.error("❌ ERREUR CRITIQUE dans assignation groupe:", error); console.error("❌ Stack trace:", error.stack); res.status(500).json({ message: "Impossible d'assigner l'utilisateur au groupe", error: error.message, details: process.env.NODE_ENV === 'development' ? error.stack : undefined }); } }); app.delete('/api/users/:userId/groups/:groupId', isAuthenticated, async (req: any, res) => { try { const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id); if (!user || user.role !== 'admin') { return res.status(403).json({ message: "Insufficient permissions" }); } const userId = req.params.userId; const groupId = parseInt(req.params.groupId); console.log('🗑️ Removing user from group:', { userId, groupId }); await storage.removeUserFromGroup(userId, groupId); console.log('✅ User removed from group successfully'); res.json({ message: "User removed from group successfully" }); } catch (error) { console.error("Error removing user from group:", error); res.status(500).json({ message: "Failed to remove user from group" }); } }); // ===== DLC PRODUCTS ROUTES ===== // DLC Products routes app.get('/api/dlc-products', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); if (!user) { return res.status(404).json({ message: "User not found" }); } const { status, supplierId } = req.query; // Determine which groups to filter by let groupIds: number[] = []; if (user.role === 'admin') { // Admin can specify a store or see all if (req.query.storeId) { groupIds = [parseInt(req.query.storeId)]; } // If no storeId specified, admin sees all (don't filter by groupIds) } else { // Non-admin users see only their assigned groups groupIds = user.userGroups.map((ug: any) => ug.group.id); } const filters: { status?: string; supplierId?: number; } = {}; if (status) filters.status = status; if (supplierId) filters.supplierId = parseInt(supplierId as string); console.log('DLC Products API called with:', { userId, userRole: user.role, groupIds: user.role === 'admin' && !req.query.storeId ? 'all' : groupIds, filters }); const dlcProducts = await storage.getDlcProducts( user.role === 'admin' && !req.query.storeId ? undefined : groupIds, filters ); console.log('DLC Products returned:', dlcProducts.length, 'items'); res.json(dlcProducts); } catch (error) { console.error("Error fetching DLC products:", error); res.status(500).json({ message: "Failed to fetch DLC products" }); } }); app.get('/api/dlc-products/stats', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); if (!user) { return res.status(404).json({ message: "User not found" }); } // Determine which groups to filter by let groupIds: number[] = []; if (user.role === 'admin') { if (req.query.storeId) { groupIds = [parseInt(req.query.storeId)]; } } else { groupIds = user.userGroups.map((ug: any) => ug.group.id); } console.log('DLC Stats API called with:', { userId, userRole: user.role, groupIds: user.role === 'admin' && !req.query.storeId ? 'all' : groupIds, storeId: req.query.storeId }); const stats = await storage.getDlcStats( user.role === 'admin' && !req.query.storeId ? undefined : groupIds ); console.log('DLC Stats returned:', stats); res.json(stats); } catch (error) { console.error("Error fetching DLC stats:", error); res.status(500).json({ message: "Failed to fetch DLC stats" }); } }); app.get('/api/dlc-products/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const dlcProduct = await storage.getDlcProduct(id); if (!dlcProduct) { return res.status(404).json({ message: "DLC Product not found" }); } // Check if user has access to this product's group const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); if (user?.role !== 'admin') { const userGroupIds = user?.userGroups.map((ug: any) => ug.group.id) || []; if (!userGroupIds.includes(dlcProduct.groupId)) { return res.status(403).json({ message: "Access denied" }); } } res.json(dlcProduct); } catch (error) { console.error("Error fetching DLC product:", error); res.status(500).json({ message: "Failed to fetch DLC product" }); } }); app.post('/api/dlc-products', isAuthenticated, async (req: any, res) => { try { console.log('📨 POST /api/dlc-products - Request body:', JSON.stringify(req.body, null, 2)); const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); if (!user) { return res.status(404).json({ message: "User not found" }); } // Validate access to the specified group if (user.role !== 'admin') { const userGroupIds = user.userGroups.map((ug: any) => ug.group.id); if (!userGroupIds.includes(req.body.groupId)) { return res.status(403).json({ message: "Access denied to this store" }); } } const validatedData = insertDlcProductFrontendSchema.parse({ ...req.body, createdBy: userId, }); const dlcProduct = await storage.createDlcProduct(validatedData); console.log('✅ DLC Product created successfully:', dlcProduct.id); res.status(201).json(dlcProduct); } catch (error) { console.error("❌ Error creating DLC product:", error); if (error instanceof z.ZodError) { return res.status(400).json({ message: "Validation error", errors: error.errors }); } res.status(500).json({ message: "Failed to create DLC product" }); } }); app.put('/api/dlc-products/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const userId = req.user.claims ? req.user.claims.sub : req.user.id; // First check if the product exists and user has access const existingProduct = await storage.getDlcProduct(id); if (!existingProduct) { return res.status(404).json({ message: "DLC Product not found" }); } const user = await storage.getUserWithGroups(userId); if (user?.role !== 'admin') { const userGroupIds = user?.userGroups.map((ug: any) => ug.group.id) || []; if (!userGroupIds.includes(existingProduct.groupId)) { return res.status(403).json({ message: "Access denied" }); } } const validatedData = insertDlcProductFrontendSchema.partial().parse(req.body); const dlcProduct = await storage.updateDlcProduct(id, validatedData); res.json(dlcProduct); } catch (error) { console.error("Error updating DLC product:", error); if (error instanceof z.ZodError) { return res.status(400).json({ message: "Validation error", errors: error.errors }); } res.status(500).json({ message: "Failed to update DLC product" }); } }); app.delete('/api/dlc-products/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const userId = req.user.claims ? req.user.claims.sub : req.user.id; // First check if the product exists and user has access const existingProduct = await storage.getDlcProduct(id); if (!existingProduct) { return res.status(404).json({ message: "DLC Product not found" }); } const user = await storage.getUserWithGroups(userId); if (user?.role !== 'admin') { const userGroupIds = user?.userGroups.map((ug: any) => ug.group.id) || []; if (!userGroupIds.includes(existingProduct.groupId)) { return res.status(403).json({ message: "Access denied" }); } } await storage.deleteDlcProduct(id); res.status(204).send(); } catch (error) { console.error("Error deleting DLC product:", error); res.status(500).json({ message: "Failed to delete DLC product" }); } }); // DLC validation routes (both POST and PUT for compatibility) app.post('/api/dlc-products/:id/validate', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const userId = req.user.claims ? req.user.claims.sub : req.user.id; // Check if user has permission to validate const user = await storage.getUser(userId); if (!user || (user.role !== 'admin' && user.role !== 'manager')) { return res.status(403).json({ message: "Insufficient permissions to validate products" }); } const dlcProduct = await storage.validateDlcProduct(id, userId); res.json(dlcProduct); } catch (error) { console.error("Error validating DLC product:", error); res.status(500).json({ message: "Failed to validate DLC product" }); } }); app.put('/api/dlc-products/:id/validate', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const userId = req.user.claims ? req.user.claims.sub : req.user.id; console.log('✅ PUT Validation request:', { id, userId }); // Check if user has permission to validate const user = await storage.getUser(userId); if (!user || (user.role !== 'admin' && user.role !== 'manager')) { return res.status(403).json({ message: "Insufficient permissions to validate products" }); } const dlcProduct = await storage.validateDlcProduct(id, userId); console.log('✅ DLC product validated via PUT:', dlcProduct.id); res.json(dlcProduct); } catch (error) { console.error("❌ Error validating DLC product via PUT:", error); res.status(500).json({ message: "Failed to validate DLC product" }); } }); // Tasks routes app.get('/api/tasks', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); const storeId = req.query.storeId; if (!user) { return res.status(404).json({ message: "User not found" }); } let groupIds: number[] | undefined; if (user.role === 'admin') { // Admin peut voir toutes les tâches ou filtrer par magasin if (storeId && storeId !== 'all') { groupIds = [parseInt(storeId)]; } // Si pas de storeId ou storeId='all', pas de filtrage (toutes les tâches) } else { // Non-admin voit seulement ses magasins assignés const userGroupIds = user.userGroups.map((ug: any) => ug.group.id); if (storeId && storeId !== 'all') { const requestedStoreId = parseInt(storeId); if (userGroupIds.includes(requestedStoreId)) { groupIds = [requestedStoreId]; } else { return res.status(403).json({ message: "Access denied to this store" }); } } else { groupIds = userGroupIds; } } const tasks = await storage.getTasks(groupIds); res.json(tasks); } catch (error) { console.error("Error fetching tasks:", error); res.status(500).json({ message: "Failed to fetch tasks" }); } }); app.get('/api/tasks/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const task = await storage.getTask(id); if (!task) { return res.status(404).json({ message: "Task not found" }); } // Check if user has access to this task's group const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); if (user?.role !== 'admin') { const userGroupIds = user?.userGroups.map((ug: any) => ug.group.id) || []; if (!userGroupIds.includes(task.groupId)) { return res.status(403).json({ message: "Access denied" }); } } res.json(task); } catch (error) { console.error("Error fetching task:", error); res.status(500).json({ message: "Failed to fetch task" }); } }); app.post('/api/tasks', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims ? req.user.claims.sub : req.user.id; const user = await storage.getUserWithGroups(userId); if (!user) { return res.status(404).json({ message: "User not found" }); } // Validate access to the specified group if (user.role !== 'admin') { const userGroupIds = user.userGroups.map((ug: any) => ug.group.id); if (!userGroupIds.includes(req.body.groupId)) { return res.status(403).json({ message: "Access denied to this store" }); } } const validatedData = insertTaskSchema.parse({ ...req.body, createdBy: userId, }); const task = await storage.createTask(validatedData); res.status(201).json(task); } catch (error) { console.error("Error creating task:", error); if (error instanceof z.ZodError) { return res.status(400).json({ message: "Validation error", errors: error.errors }); } res.status(500).json({ message: "Failed to create task" }); } }); app.put('/api/tasks/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const userId = req.user.claims ? req.user.claims.sub : req.user.id; const existingTask = await storage.getTask(id); if (!existingTask) { return res.status(404).json({ message: "Task not found" }); } const user = await storage.getUserWithGroups(userId); if (user?.role !== 'admin') { const userGroupIds = user?.userGroups.map((ug: any) => ug.group.id) || []; if (!userGroupIds.includes(existingTask.groupId)) { return res.status(403).json({ message: "Access denied" }); } // Non-admin users can only edit their own tasks if (existingTask.createdBy !== userId) { return res.status(403).json({ message: "Can only edit your own tasks" }); } } const validatedData = insertTaskSchema.partial().parse(req.body); const updatedTask = await storage.updateTask(id, validatedData); res.json(updatedTask); } catch (error) { console.error("Error updating task:", error); if (error instanceof z.ZodError) { return res.status(400).json({ message: "Validation error", errors: error.errors }); } res.status(500).json({ message: "Failed to update task" }); } }); app.post('/api/tasks/:id/complete', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const userId = req.user.claims ? req.user.claims.sub : req.user.id; console.log("🎯 Task completion request:", { id, userId }); const existingTask = await storage.getTask(id); if (!existingTask) { console.log("❌ Task not found:", id); return res.status(404).json({ message: "Task not found" }); } console.log("✅ Task found:", existingTask); const user = await storage.getUserWithGroups(userId); console.log("👤 User data:", user); if (user?.role !== 'admin') { const userGroupIds = user?.userGroups.map((ug: any) => ug.group.id) || []; if (!userGroupIds.includes(existingTask.groupId)) { console.log("❌ Access denied - group mismatch:", { userGroupIds, taskGroupId: existingTask.groupId }); return res.status(403).json({ message: "Access denied" }); } } console.log("🔄 Completing task using storage.completeTask..."); await storage.completeTask(id, userId); // Get the updated task to return it const updatedTask = await storage.getTask(id); console.log("✅ Task completed successfully:", updatedTask); res.json(updatedTask); } catch (error) { console.error("❌ Error completing task:", error); res.status(500).json({ message: "Failed to complete task", error: error.message }); } }); app.delete('/api/tasks/:id', isAuthenticated, async (req: any, res) => { try { const id = parseInt(req.params.id); const userId = req.user.claims ? req.user.claims.sub : req.user.id; const existingTask = await storage.getTask(id); if (!existingTask) { return res.status(404).json({ message: "Task not found" }); } const user = await storage.getUserWithGroups(userId); if (user?.role !== 'admin') { const userGroupIds = user?.userGroups.map((ug: any) => ug.group.id) || []; if (!userGroupIds.includes(existingTask.groupId)) { return res.status(403).json({ message: "Access denied" }); } if (existingTask.createdBy !== userId) { return res.status(403).json({ message: "Can only delete your own tasks" }); } } await storage.deleteTask(id); res.status(204).send(); } catch (error) { console.error("Error deleting task:", error); res.status(500).json({ message: "Failed to delete task" }); } }); const server = createServer(app); return server; }