mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
2073 lines
81 KiB
TypeScript
2073 lines
81 KiB
TypeScript
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<Server> {
|
|
// 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;
|
|
} |