diff --git a/server/routers.ts b/server/routers.ts index e84aff0..fb52042 100644 --- a/server/routers.ts +++ b/server/routers.ts @@ -428,9 +428,28 @@ export const appRouter = router({ let signatureName: string | null = null; try { - if (!invoice.fileKey) throw new Error('Fichier PDF source introuvable'); - const sourcePath = path.join(STORAGE_BASE_PATH, invoice.fileKey); - const pdfBytes = await fs.readFile(sourcePath); + if (!invoice.fileKey && !invoice.fileUrl) throw new Error('Fichier PDF source introuvable'); + let pdfBytes: Buffer; + // Essayer d'abord le chemin local, puis l'URL publique + const sourcePath = path.join(STORAGE_BASE_PATH, invoice.fileKey || ''); + try { + pdfBytes = await fs.readFile(sourcePath); + } catch (_localErr) { + // Fichier non disponible localement → télécharger depuis l'URL + const fileUrl = invoice.fileUrl; + if (!fileUrl) throw new Error('Fichier PDF source introuvable (local et URL)'); + // Construire l'URL absolue si relative + let absoluteUrl = fileUrl; + if (fileUrl.startsWith('/')) { + const baseUrl = process.env.APP_BASE_URL || `http://localhost:${process.env.PORT || 3000}`; + absoluteUrl = `${baseUrl}${fileUrl}`; + } + // Node.js 22 a fetch natif + const response = await fetch(absoluteUrl); + if (!response.ok) throw new Error(`Impossible de télécharger le PDF: ${response.status}`); + const arrayBuffer = await response.arrayBuffer(); + pdfBytes = Buffer.from(arrayBuffer); + } const pdfDoc = await PDFDocument.load(pdfBytes); const pages = pdfDoc.getPages(); const lastPage = pages[pages.length - 1]; @@ -512,8 +531,23 @@ export const appRouter = router({ if (sig) { signatureName = `${sig.firstName} ${sig.lastName}`; try { + let sigImageBytes: Buffer; const sigImagePath = path.join(STORAGE_BASE_PATH, sig.imageKey); - const sigImageBytes = await fs.readFile(sigImagePath); + try { + sigImageBytes = await fs.readFile(sigImagePath); + } catch (_sigLocalErr) { + // Fichier signature non disponible localement → télécharger depuis l'URL + const sigUrl = sig.imageUrl; + if (!sigUrl) throw new Error('Image signature introuvable'); + let absoluteSigUrl = sigUrl; + if (sigUrl.startsWith('/')) { + const baseUrl = process.env.APP_BASE_URL || `http://localhost:${process.env.PORT || 3000}`; + absoluteSigUrl = `${baseUrl}${sigUrl}`; + } + const sigResp = await fetch(absoluteSigUrl); + if (!sigResp.ok) throw new Error(`Impossible de télécharger la signature: ${sigResp.status}`); + sigImageBytes = Buffer.from(await sigResp.arrayBuffer()); + } const mimeType = sig.imageKey.toLowerCase().endsWith('.png') ? 'image/png' : 'image/jpeg'; let embeddedSig; if (mimeType === 'image/png') { @@ -835,15 +869,35 @@ export const appRouter = router({ const sig = await getSignatureById(assoc.signatureId); if (sig) { const sigImagePath = path.join(STORAGE_BASE_PATH, sig.imageKey); - const pdfBytes = await fs.readFile(sourcePath); + // Lire le PDF source (local ou URL) + let pdfBytes: Buffer; + try { + pdfBytes = await fs.readFile(sourcePath); + } catch (_) { + const fileUrl = invoice!.fileUrl; + if (!fileUrl) throw new Error('PDF source introuvable'); + let absUrl = fileUrl.startsWith('/') ? `${process.env.APP_BASE_URL || `http://localhost:${process.env.PORT || 3000}`}${fileUrl}` : fileUrl; + const r = await fetch(absUrl); + if (!r.ok) throw new Error(`HTTP ${r.status}`); + pdfBytes = Buffer.from(await r.arrayBuffer()); + } const pdfDoc = await PDFDocument.load(pdfBytes); const pages = pdfDoc.getPages(); const lastPage = pages[pages.length - 1]; const { width, height } = lastPage.getSize(); - // Read signature image - const sigImageBytes = await fs.readFile(sigImagePath); - const sigImageBase64 = sigImageBytes.toString('base64'); + // Read signature image (local ou URL) + let sigImageBytes: Buffer; + try { + sigImageBytes = await fs.readFile(sigImagePath); + } catch (_) { + const sigUrl = sig.imageUrl; + if (!sigUrl) throw new Error('Signature introuvable'); + let absSigUrl = sigUrl.startsWith('/') ? `${process.env.APP_BASE_URL || `http://localhost:${process.env.PORT || 3000}`}${sigUrl}` : sigUrl; + const sr = await fetch(absSigUrl); + if (!sr.ok) throw new Error(`HTTP ${sr.status}`); + sigImageBytes = Buffer.from(await sr.arrayBuffer()); + } const mimeType = sig.imageKey.toLowerCase().endsWith('.png') ? 'image/png' : 'image/jpeg'; let embeddedSig; @@ -880,17 +934,32 @@ export const appRouter = router({ console.log(`[Export] Signature apposée pour ${serviceName} sur ${filename}`); } else { // Signature not found, copy without signature - await fs.copyFile(sourcePath, destPath); + try { await fs.copyFile(sourcePath, destPath); } catch (_) { + const fu = invoice!.fileUrl; if (!fu) throw new Error('PDF introuvable'); + const au = fu.startsWith('/') ? `${process.env.APP_BASE_URL||`http://localhost:${process.env.PORT||3000}`}${fu}` : fu; + const rb = await fetch(au); if (!rb.ok) throw new Error(`HTTP ${rb.status}`); + await fs.writeFile(destPath, Buffer.from(await rb.arrayBuffer())); + } copiedFiles.push(destPath); } } catch (sigError: any) { console.warn(`[Export] Impossible d'apposer la signature: ${sigError.message}. Copie sans signature.`); - await fs.copyFile(sourcePath, destPath); + try { await fs.copyFile(sourcePath, destPath); } catch (_) { + const fu = invoice!.fileUrl; if (!fu) throw new Error('PDF introuvable'); + const au = fu.startsWith('/') ? `${process.env.APP_BASE_URL||`http://localhost:${process.env.PORT||3000}`}${fu}` : fu; + const rb = await fetch(au); if (!rb.ok) throw new Error(`HTTP ${rb.status}`); + await fs.writeFile(destPath, Buffer.from(await rb.arrayBuffer())); + } copiedFiles.push(destPath); } } else { // No signature association, copy as-is - await fs.copyFile(sourcePath, destPath); + try { await fs.copyFile(sourcePath, destPath); } catch (_) { + const fu = invoice!.fileUrl; if (!fu) throw new Error('PDF introuvable'); + const au = fu.startsWith('/') ? `${process.env.APP_BASE_URL||`http://localhost:${process.env.PORT||3000}`}${fu}` : fu; + const rb = await fetch(au); if (!rb.ok) throw new Error(`HTTP ${rb.status}`); + await fs.writeFile(destPath, Buffer.from(await rb.arrayBuffer())); + } copiedFiles.push(destPath); } diff --git a/todo.md b/todo.md index 33ea972..b5e3d11 100644 --- a/todo.md +++ b/todo.md @@ -597,3 +597,9 @@ - [x] Ajouter table bapHistory en base de données (schéma + migration) - [x] Enregistrer chaque validation BAP dans l'historique - [x] Ajouter la route dans App.tsx et le lien dans le menu Traçabilité + +## Correction génération PDF BAP (fallback URL) +- [x] Corriger la lecture du PDF source dans validateBAP (fallback URL si fichier local absent) +- [x] Corriger la lecture de l'image de signature dans validateBAP (fallback URL) +- [x] Corriger la lecture du PDF source dans exportToPdf (fallback URL) +- [x] Corriger la lecture de l'image de signature dans exportToPdf (fallback URL)