mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
6 files changed
+138
-17
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
@@ -513,26 +513,37 @@ export default function BLReconciliation() {
|
|||||||
startProcessingTimer();
|
startProcessingTimer();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
// Convertir le fichier en base64 pour l'envoyer via notre route backend
|
||||||
formData.append('file', selectedFile);
|
const fileBase64 = await new Promise<string>((resolve, reject) => {
|
||||||
formData.append('supplier', selectedDeliveryForInvoice.supplier?.name || '');
|
const reader = new FileReader();
|
||||||
formData.append('blNumber', selectedDeliveryForInvoice.blNumber || '');
|
reader.onload = () => {
|
||||||
formData.append('type', 'Facture');
|
const base64 = (reader.result as string).split(',')[1]; // Retirer le préfixe data:...
|
||||||
|
resolve(base64);
|
||||||
// Créer un AbortController pour gérer le timeout
|
};
|
||||||
const controller = new AbortController();
|
reader.onerror = reject;
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 secondes
|
reader.readAsDataURL(selectedFile);
|
||||||
|
|
||||||
const response = await fetch(selectedDeliveryForInvoice.group.webhookUrl, {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData,
|
|
||||||
signal: controller.signal,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
// Préparer les données pour la route backend proxy
|
||||||
|
const payload = {
|
||||||
|
webhookUrl: selectedDeliveryForInvoice.group.webhookUrl,
|
||||||
|
formDataFields: {
|
||||||
|
file: {
|
||||||
|
base64: fileBase64,
|
||||||
|
filename: selectedFile.name,
|
||||||
|
contentType: selectedFile.type
|
||||||
|
},
|
||||||
|
supplier: selectedDeliveryForInvoice.supplier?.name || '',
|
||||||
|
blNumber: selectedDeliveryForInvoice.blNumber || '',
|
||||||
|
type: 'Facture'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (!response.ok) {
|
// Envoyer via la route backend proxy (évite les problèmes CORS)
|
||||||
throw new Error(`Erreur ${response.status}: ${response.statusText}`);
|
const response = await apiRequest('/api/reconciliation/send-invoice-webhook', 'POST', payload);
|
||||||
|
|
||||||
|
if (!response || !response.success) {
|
||||||
|
throw new Error(response?.error || 'Erreur lors de l\'envoi');
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCloseWaitingModal();
|
handleCloseWaitingModal();
|
||||||
|
|||||||
Generated
+11
@@ -41,6 +41,7 @@
|
|||||||
"@radix-ui/react-tooltip": "^1.2.0",
|
"@radix-ui/react-tooltip": "^1.2.0",
|
||||||
"@tanstack/react-query": "^5.60.5",
|
"@tanstack/react-query": "^5.60.5",
|
||||||
"@types/bcrypt": "^6.0.0",
|
"@types/bcrypt": "^6.0.0",
|
||||||
|
"@types/form-data": "^2.2.1",
|
||||||
"@types/memoizee": "^0.4.12",
|
"@types/memoizee": "^0.4.12",
|
||||||
"@types/node-fetch": "^2.6.13",
|
"@types/node-fetch": "^2.6.13",
|
||||||
"@types/pg": "^8.15.4",
|
"@types/pg": "^8.15.4",
|
||||||
@@ -58,6 +59,7 @@
|
|||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"express-rate-limit": "^7.5.1",
|
"express-rate-limit": "^7.5.1",
|
||||||
"express-session": "^1.18.1",
|
"express-session": "^1.18.1",
|
||||||
|
"form-data": "^4.0.4",
|
||||||
"framer-motion": "^11.13.1",
|
"framer-motion": "^11.13.1",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
@@ -3750,6 +3752,15 @@
|
|||||||
"@types/express": "*"
|
"@types/express": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/form-data": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/http-errors": {
|
"node_modules/@types/http-errors": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
"@radix-ui/react-tooltip": "^1.2.0",
|
"@radix-ui/react-tooltip": "^1.2.0",
|
||||||
"@tanstack/react-query": "^5.60.5",
|
"@tanstack/react-query": "^5.60.5",
|
||||||
"@types/bcrypt": "^6.0.0",
|
"@types/bcrypt": "^6.0.0",
|
||||||
|
"@types/form-data": "^2.2.1",
|
||||||
"@types/memoizee": "^0.4.12",
|
"@types/memoizee": "^0.4.12",
|
||||||
"@types/node-fetch": "^2.6.13",
|
"@types/node-fetch": "^2.6.13",
|
||||||
"@types/pg": "^8.15.4",
|
"@types/pg": "^8.15.4",
|
||||||
@@ -60,6 +61,7 @@
|
|||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"express-rate-limit": "^7.5.1",
|
"express-rate-limit": "^7.5.1",
|
||||||
"express-session": "^1.18.1",
|
"express-session": "^1.18.1",
|
||||||
|
"form-data": "^4.0.4",
|
||||||
"framer-motion": "^11.13.1",
|
"framer-motion": "^11.13.1",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
|
|||||||
@@ -788,6 +788,103 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Route proxy pour envoi de factures vers webhook (accessible admin + directeur)
|
||||||
|
app.post('/api/reconciliation/send-invoice-webhook', isAuthenticated, async (req: any, res) => {
|
||||||
|
try {
|
||||||
|
const userId = req.user?.claims?.sub || req.user?.id;
|
||||||
|
if (!userId) {
|
||||||
|
return res.status(401).json({ error: 'Utilisateur non authentifié' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await storage.getUserWithGroups(userId);
|
||||||
|
if (!user) {
|
||||||
|
return res.status(404).json({ error: 'Utilisateur non trouvé' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autoriser admin ET directeur
|
||||||
|
if (user.role !== 'admin' && user.role !== 'directeur') {
|
||||||
|
return res.status(403).json({ error: 'Accès refusé - Admin ou Directeur uniquement' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { webhookUrl, formDataFields } = req.body;
|
||||||
|
|
||||||
|
if (!webhookUrl || !formDataFields) {
|
||||||
|
return res.status(400).json({ error: 'Données manquantes: webhookUrl ou formDataFields' });
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('📤 INVOICE: Envoi facture via webhook', {
|
||||||
|
webhookUrl: webhookUrl.substring(0, 50) + '...',
|
||||||
|
userId: user.id,
|
||||||
|
userRole: user.role,
|
||||||
|
fieldsCount: Object.keys(formDataFields).length
|
||||||
|
});
|
||||||
|
|
||||||
|
// Préparer le FormData pour l'envoi au webhook externe
|
||||||
|
const FormData = (await import('form-data')).default;
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
// Ajouter tous les champs du formData
|
||||||
|
for (const [key, value] of Object.entries(formDataFields)) {
|
||||||
|
if (key === 'file' && typeof value === 'object' && (value as any).base64) {
|
||||||
|
// Reconstituer le fichier depuis base64
|
||||||
|
const fileData = value as { base64: string; filename: string; contentType: string };
|
||||||
|
const buffer = Buffer.from(fileData.base64, 'base64');
|
||||||
|
formData.append('file', buffer, {
|
||||||
|
filename: fileData.filename,
|
||||||
|
contentType: fileData.contentType
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
formData.append(key, value as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 secondes
|
||||||
|
|
||||||
|
const response = await fetch(webhookUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData as any,
|
||||||
|
headers: formData.getHeaders(),
|
||||||
|
signal: controller.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
console.error('❌ INVOICE: Erreur webhook', { status: response.status, errorText: errorText.substring(0, 200) });
|
||||||
|
throw new Error(`Erreur webhook: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.text();
|
||||||
|
console.log('✅ INVOICE: Webhook réussi', { userId: user.id, userRole: user.role });
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Facture envoyée avec succès',
|
||||||
|
webhookResponse: result
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('❌ INVOICE: Erreur envoi facture:', {
|
||||||
|
name: error.name,
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
|
||||||
|
let errorMessage = 'Erreur lors de l\'envoi de la facture';
|
||||||
|
if (error.name === 'AbortError') {
|
||||||
|
errorMessage = 'Timeout - Le traitement a pris trop de temps';
|
||||||
|
} else if (error.message) {
|
||||||
|
errorMessage = error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
error: errorMessage,
|
||||||
|
details: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Auth routes handled by authSwitch (local or Replit)
|
// Auth routes handled by authSwitch (local or Replit)
|
||||||
|
|
||||||
// Groups routes
|
// Groups routes
|
||||||
|
|||||||
Reference in new issue
Block a user