mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-22 14:38:19 +02:00
Merge pull request #333 from R0m1k3/1.1
Update webhook integration to use JSON for sending PDF data
This commit is contained in:
6 files changed
+235
-89
No files matched your search
@@ -37,4 +37,4 @@ args = "npm run dev"
|
|||||||
waitForPort = 5000
|
waitForPort = 5000
|
||||||
|
|
||||||
[agent]
|
[agent]
|
||||||
integrations = ["javascript_object_storage==1.0.0", "javascript_database==1.0.0", "javascript_log_in_with_replit==1.0.0"]
|
integrations = ["javascript_object_storage==1.0.0", "javascript_log_in_with_replit==1.0.0", "javascript_database==1.0.0"]
|
||||||
+195
@@ -0,0 +1,195 @@
|
|||||||
|
|
||||||
|
|
||||||
|
dbHost: 'logiflow-db'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
🐳 PRODUCTION: Using standard PostgreSQL
|
||||||
|
|
||||||
|
✅ PostgreSQL connection test successful
|
||||||
|
|
||||||
|
🔗 Database initialization: {
|
||||||
|
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
|
||||||
|
isProduction: true,
|
||||||
|
|
||||||
|
hasDbUrl: true,
|
||||||
|
|
||||||
|
dbHost: 'logiflow-db:5432'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
✅ Using DatabaseStorage (production PostgreSQL)
|
||||||
|
|
||||||
|
⏰ Automatic backup scheduled for daily 2:00 AM+ (native timer)
|
||||||
|
|
||||||
|
file:///app/dist/index.js:11
|
||||||
|
|
||||||
|
throw Error('Dynamic require of "' + x2 + '" is not supported');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
^
|
||||||
|
|
||||||
|
Error: Dynamic require of "stream" is not supported
|
||||||
|
|
||||||
|
at file:///app/dist/index.js:11:9
|
||||||
|
|
||||||
|
at node_modules/busboy/lib/types/multipart.js (file:///app/dist/index.js:7398:34)
|
||||||
|
|
||||||
|
at __require2 (file:///app/dist/index.js:17:50)
|
||||||
|
|
||||||
|
at node_modules/busboy/lib/index.js (file:///app/dist/index.js:9024:7)
|
||||||
|
|
||||||
|
at __require2 (file:///app/dist/index.js:17:50)
|
||||||
|
|
||||||
|
at file:///app/dist/index.js:25027:29
|
||||||
|
|
||||||
|
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
|
||||||
|
|
||||||
|
Node.js v20.19.4
|
||||||
|
|
||||||
|
Applying DLC migration...
|
||||||
|
|
||||||
|
NOTICE: column "stock_epuise" of relation "dlc_products" already exists, skipping
|
||||||
|
|
||||||
|
NOTICE: column "stock_epuise_by" of relation "dlc_products" already exists, skipping
|
||||||
|
|
||||||
|
NOTICE: column "stock_epuise_at" of relation "dlc_products" already exists, skipping
|
||||||
|
|
||||||
|
NOTICE: relation "idx_dlc_products_stock_epuise" already exists, skipping
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
|
||||||
|
␍CREATE INDEX
|
||||||
|
|
||||||
|
🔗 Database initialization: {
|
||||||
|
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
|
||||||
|
isProduction: true,
|
||||||
|
|
||||||
|
hasDbUrl: true,
|
||||||
|
|
||||||
|
dbHost: 'logiflow-db'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
🐳 PRODUCTION: Using standard PostgreSQL
|
||||||
|
|
||||||
|
✅ PostgreSQL connection test successful
|
||||||
|
|
||||||
|
🔗 Database initialization: {
|
||||||
|
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
|
||||||
|
isProduction: true,
|
||||||
|
|
||||||
|
hasDbUrl: true,
|
||||||
|
|
||||||
|
dbHost: 'logiflow-db:5432'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
✅ Using DatabaseStorage (production PostgreSQL)
|
||||||
|
|
||||||
|
⏰ Automatic backup scheduled for daily 2:00 AM+ (native timer)
|
||||||
|
|
||||||
|
file:///app/dist/index.js:11
|
||||||
|
|
||||||
|
throw Error('Dynamic require of "' + x2 + '" is not supported');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
^
|
||||||
|
|
||||||
|
Error: Dynamic require of "stream" is not supported
|
||||||
|
|
||||||
|
at file:///app/dist/index.js:11:9
|
||||||
|
|
||||||
|
at node_modules/busboy/lib/types/multipart.js (file:///app/dist/index.js:7398:34)
|
||||||
|
|
||||||
|
at __require2 (file:///app/dist/index.js:17:50)
|
||||||
|
|
||||||
|
at node_modules/busboy/lib/index.js (file:///app/dist/index.js:9024:7)
|
||||||
|
|
||||||
|
at __require2 (file:///app/dist/index.js:17:50)
|
||||||
|
|
||||||
|
at file:///app/dist/index.js:25027:29
|
||||||
|
|
||||||
|
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
|
||||||
|
|
||||||
|
Node.js v20.19.4
|
||||||
|
|
||||||
|
Applying DLC migration...
|
||||||
|
|
||||||
|
NOTICE: column "stock_epuise" of relation "dlc_products" already exists, skipping
|
||||||
|
|
||||||
|
NOTICE: column "stock_epuise_by" of relation "dlc_products" already exists, skipping
|
||||||
|
|
||||||
|
NOTICE: column "stock_epuise_at" of relation "dlc_products" already exists, skipping
|
||||||
|
|
||||||
|
NOTICE: relation "idx_dlc_products_stock_epuise" already exists, skipping
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
|
||||||
|
␍CREATE INDEX
|
||||||
|
|
||||||
|
🔗 Database initialization: {
|
||||||
|
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
|
||||||
|
isProduction: true,
|
||||||
|
|
||||||
|
hasDbUrl: true,
|
||||||
|
|
||||||
|
dbHost: 'logiflow-db'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
🐳 PRODUCTION: Using standard PostgreSQL
|
||||||
|
|
||||||
|
✅ PostgreSQL connection test successful
|
||||||
|
|
||||||
|
🔗 Database initialization: {
|
||||||
|
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
|
||||||
|
isProduction: true,
|
||||||
|
|
||||||
|
hasDbUrl: true,
|
||||||
|
|
||||||
|
dbHost: 'logiflow-db:5432'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
✅ Using DatabaseStorage (production PostgreSQL)
|
||||||
|
|
||||||
|
⏰ Automatic backup scheduled for daily 2:00 AM+ (native timer)
|
||||||
|
|
||||||
|
file:///app/dist/index.js:11
|
||||||
|
|
||||||
|
throw Error('Dynamic require of "' + x2 + '" is not supported');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
^
|
||||||
|
|
||||||
|
Error: Dynamic require of "stream" is not supported
|
||||||
|
|
||||||
|
at file:///app/dist/index.js:11:9
|
||||||
|
|
||||||
|
at node_modules/busboy/lib/types/multipart.js (file:///app/dist/index.js:7398:34)
|
||||||
|
|
||||||
|
at __require2 (file:///app/dist/index.js:17:50)
|
||||||
|
|
||||||
|
at node_modules/busboy/lib/index.js (file:///app/dist/index.js:9024:7)
|
||||||
|
|
||||||
|
at __require2 (file:///app/dist/index.js:17:50)
|
||||||
|
|
||||||
|
at file:///app/dist/index.js:25027:29
|
||||||
|
|
||||||
|
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
|
||||||
|
|
||||||
|
Node.js v20.19.4
|
||||||
@@ -102,9 +102,24 @@ export default function Sidebar() {
|
|||||||
startProcessingTimer();
|
startProcessingTimer();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
// Convertir le fichier en base64
|
||||||
formData.append('pdf', selectedFile);
|
const pdfBase64 = await new Promise<string>((resolve, reject) => {
|
||||||
formData.append('recipient', selectedRecipient);
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
const result = reader.result as string;
|
||||||
|
// Enlever le préfixe "data:application/pdf;base64,"
|
||||||
|
const base64 = result.split(',')[1];
|
||||||
|
resolve(base64);
|
||||||
|
};
|
||||||
|
reader.onerror = () => reject(new Error('Erreur lors de la lecture du fichier'));
|
||||||
|
reader.readAsDataURL(selectedFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
const requestBody = {
|
||||||
|
pdfBase64,
|
||||||
|
fileName: selectedFile.name,
|
||||||
|
recipient: selectedRecipient
|
||||||
|
};
|
||||||
|
|
||||||
// Créer un AbortController pour gérer le timeout
|
// Créer un AbortController pour gérer le timeout
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
@@ -112,7 +127,10 @@ export default function Sidebar() {
|
|||||||
|
|
||||||
const response = await fetch('/api/bap/send-webhook', {
|
const response = await fetch('/api/bap/send-webhook', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData,
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(requestBody),
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
credentials: 'include'
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
|
|||||||
Generated
-30
@@ -41,13 +41,11 @@
|
|||||||
"@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/busboy": "^1.5.4",
|
|
||||||
"@types/form-data": "^2.2.1",
|
"@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",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
"busboy": "^1.6.0",
|
|
||||||
"caniuse-lite": "^1.0.30001727",
|
"caniuse-lite": "^1.0.30001727",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
@@ -3625,15 +3623,6 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/busboy": {
|
|
||||||
"version": "1.5.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.4.tgz",
|
|
||||||
"integrity": "sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/connect": {
|
"node_modules/@types/connect": {
|
||||||
"version": "3.4.38",
|
"version": "3.4.38",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
||||||
@@ -4232,17 +4221,6 @@
|
|||||||
"node": ">=6.14.2"
|
"node": ">=6.14.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/busboy": {
|
|
||||||
"version": "1.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
|
||||||
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
|
||||||
"dependencies": {
|
|
||||||
"streamsearch": "^1.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.16.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
@@ -8450,14 +8428,6 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/streamsearch": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
|
|||||||
@@ -43,13 +43,11 @@
|
|||||||
"@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/busboy": "^1.5.4",
|
|
||||||
"@types/form-data": "^2.2.1",
|
"@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",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
"busboy": "^1.6.0",
|
|
||||||
"caniuse-lite": "^1.0.30001727",
|
"caniuse-lite": "^1.0.30001727",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|||||||
+17
-52
@@ -49,7 +49,6 @@ import { eq, desc, or, isNull } from "drizzle-orm";
|
|||||||
import { invoiceVerificationService } from "./invoiceVerification";
|
import { invoiceVerificationService } from "./invoiceVerification";
|
||||||
import { backupService } from "./backupService";
|
import { backupService } from "./backupService";
|
||||||
import { weatherService } from "./weatherService.js";
|
import { weatherService } from "./weatherService.js";
|
||||||
import Busboy from 'busboy';
|
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
|
|
||||||
export async function registerRoutes(app: Express): Promise<Server> {
|
export async function registerRoutes(app: Express): Promise<Server> {
|
||||||
@@ -78,69 +77,35 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
return res.status(403).json({ error: 'Accès refusé - Admin uniquement' });
|
return res.status(403).json({ error: 'Accès refusé - Admin uniquement' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parser le multipart/form-data avec busboy
|
// Récupérer les données JSON du body
|
||||||
const busboy = Busboy({ headers: req.headers });
|
const { pdfBase64, fileName, recipient } = req.body;
|
||||||
let fileBuffer: Buffer | null = null;
|
|
||||||
let fileName = '';
|
|
||||||
let recipient = '';
|
|
||||||
let mimeType = '';
|
|
||||||
|
|
||||||
const parsePromise = new Promise<void>((resolve, reject) => {
|
|
||||||
busboy.on('file', (fieldname, file, info) => {
|
|
||||||
const { filename, mimeType: fileMimeType } = info;
|
|
||||||
|
|
||||||
if (fieldname === 'pdf') {
|
|
||||||
fileName = filename;
|
|
||||||
mimeType = fileMimeType;
|
|
||||||
|
|
||||||
const chunks: Buffer[] = [];
|
|
||||||
file.on('data', (chunk) => {
|
|
||||||
chunks.push(chunk);
|
|
||||||
});
|
|
||||||
|
|
||||||
file.on('end', () => {
|
|
||||||
fileBuffer = Buffer.concat(chunks);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
file.resume(); // Ignorer les autres fichiers
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
busboy.on('field', (fieldname, val) => {
|
|
||||||
if (fieldname === 'recipient') {
|
|
||||||
recipient = val;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
busboy.on('finish', () => {
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
busboy.on('error', (err) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
req.pipe(busboy);
|
|
||||||
await parsePromise;
|
|
||||||
|
|
||||||
// Valider les données
|
// Valider les données
|
||||||
if (!fileBuffer) {
|
if (!pdfBase64 || !fileName || !recipient) {
|
||||||
return res.status(400).json({ error: 'Aucun fichier PDF fourni' });
|
return res.status(400).json({ error: 'Données manquantes: pdfBase64, fileName ou recipient' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mimeType !== 'application/pdf') {
|
if (!['Prissela', 'Célia'].includes(recipient)) {
|
||||||
|
return res.status(400).json({ error: 'Destinataire invalide' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier l'extension PDF
|
||||||
|
if (!fileName.toLowerCase().endsWith('.pdf')) {
|
||||||
return res.status(400).json({ error: 'Le fichier doit être un PDF' });
|
return res.status(400).json({ error: 'Le fichier doit être un PDF' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!recipient || !['Prissela', 'Célia'].includes(recipient)) {
|
// Décoder le base64 en buffer
|
||||||
return res.status(400).json({ error: 'Destinataire invalide' });
|
let fileBuffer: Buffer;
|
||||||
|
try {
|
||||||
|
fileBuffer = Buffer.from(pdfBase64, 'base64');
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(400).json({ error: 'Format base64 invalide' });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('📤 BAP: Envoi webhook n8n', {
|
console.log('📤 BAP: Envoi webhook n8n', {
|
||||||
recipient,
|
recipient,
|
||||||
fileName,
|
fileName,
|
||||||
fileSize: fileBuffer?.length || 0,
|
fileSize: fileBuffer.length,
|
||||||
userId: user.id
|
userId: user.id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user