Checkpoint: Correction critique : la route validateBAP et exportToPdf lisent maintenant le PDF source et l'image de signature depuis l'URL publique en fallback si le fichier local n'est pas disponible (cas du VPS où les fichiers sont stockés localement mais pas dans la sandbox).
This commit is contained in:
@@ -428,9 +428,28 @@ export const appRouter = router({
|
|||||||
let signatureName: string | null = null;
|
let signatureName: string | null = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!invoice.fileKey) throw new Error('Fichier PDF source introuvable');
|
if (!invoice.fileKey && !invoice.fileUrl) throw new Error('Fichier PDF source introuvable');
|
||||||
const sourcePath = path.join(STORAGE_BASE_PATH, invoice.fileKey);
|
let pdfBytes: Buffer;
|
||||||
const pdfBytes = await fs.readFile(sourcePath);
|
// 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 pdfDoc = await PDFDocument.load(pdfBytes);
|
||||||
const pages = pdfDoc.getPages();
|
const pages = pdfDoc.getPages();
|
||||||
const lastPage = pages[pages.length - 1];
|
const lastPage = pages[pages.length - 1];
|
||||||
@@ -512,8 +531,23 @@ export const appRouter = router({
|
|||||||
if (sig) {
|
if (sig) {
|
||||||
signatureName = `${sig.firstName} ${sig.lastName}`;
|
signatureName = `${sig.firstName} ${sig.lastName}`;
|
||||||
try {
|
try {
|
||||||
|
let sigImageBytes: Buffer;
|
||||||
const sigImagePath = path.join(STORAGE_BASE_PATH, sig.imageKey);
|
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';
|
const mimeType = sig.imageKey.toLowerCase().endsWith('.png') ? 'image/png' : 'image/jpeg';
|
||||||
let embeddedSig;
|
let embeddedSig;
|
||||||
if (mimeType === 'image/png') {
|
if (mimeType === 'image/png') {
|
||||||
@@ -835,15 +869,35 @@ export const appRouter = router({
|
|||||||
const sig = await getSignatureById(assoc.signatureId);
|
const sig = await getSignatureById(assoc.signatureId);
|
||||||
if (sig) {
|
if (sig) {
|
||||||
const sigImagePath = path.join(STORAGE_BASE_PATH, sig.imageKey);
|
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 pdfDoc = await PDFDocument.load(pdfBytes);
|
||||||
const pages = pdfDoc.getPages();
|
const pages = pdfDoc.getPages();
|
||||||
const lastPage = pages[pages.length - 1];
|
const lastPage = pages[pages.length - 1];
|
||||||
const { width, height } = lastPage.getSize();
|
const { width, height } = lastPage.getSize();
|
||||||
|
|
||||||
// Read signature image
|
// Read signature image (local ou URL)
|
||||||
const sigImageBytes = await fs.readFile(sigImagePath);
|
let sigImageBytes: Buffer;
|
||||||
const sigImageBase64 = sigImageBytes.toString('base64');
|
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';
|
const mimeType = sig.imageKey.toLowerCase().endsWith('.png') ? 'image/png' : 'image/jpeg';
|
||||||
|
|
||||||
let embeddedSig;
|
let embeddedSig;
|
||||||
@@ -880,17 +934,32 @@ export const appRouter = router({
|
|||||||
console.log(`[Export] Signature apposée pour ${serviceName} sur ${filename}`);
|
console.log(`[Export] Signature apposée pour ${serviceName} sur ${filename}`);
|
||||||
} else {
|
} else {
|
||||||
// Signature not found, copy without signature
|
// 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);
|
copiedFiles.push(destPath);
|
||||||
}
|
}
|
||||||
} catch (sigError: any) {
|
} catch (sigError: any) {
|
||||||
console.warn(`[Export] Impossible d'apposer la signature: ${sigError.message}. Copie sans signature.`);
|
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);
|
copiedFiles.push(destPath);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No signature association, copy as-is
|
// 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);
|
copiedFiles.push(destPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
todo.md
6
todo.md
@@ -597,3 +597,9 @@
|
|||||||
- [x] Ajouter table bapHistory en base de données (schéma + migration)
|
- [x] Ajouter table bapHistory en base de données (schéma + migration)
|
||||||
- [x] Enregistrer chaque validation BAP dans l'historique
|
- [x] Enregistrer chaque validation BAP dans l'historique
|
||||||
- [x] Ajouter la route dans App.tsx et le lien dans le menu Traçabilité
|
- [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)
|
||||||
|
|||||||
Reference in New Issue
Block a user