Checkpoint: Ajout du téléchargement groupé ZIP dans Factures BAP, bouton Re-générer dans Historique BAP, filtres Validées BAP / En attente BAP dans Factures BAP

This commit is contained in:
Manus
2026-04-12 17:53:38 -04:00
parent ba3392b1e4
commit 412da0e68f
7 changed files with 942 additions and 8 deletions

View File

@@ -4,6 +4,7 @@ import { createServer } from "http";
import net from "net";
import path from "path";
import fs from "fs";
import archiver from "archiver";
import { createExpressMiddleware } from "@trpc/server/adapters/express";
import { registerOAuthRoutes } from "./oauth";
import { appRouter } from "../routers";
@@ -97,6 +98,47 @@ async function startServer() {
res.sendFile(found);
});
// Route de téléchargement groupé ZIP des PDFs annotés BAP
// POST /api/download-bap-zip avec body { files: Array<{ pdfPath: string, filename: string }> }
app.post("/api/download-bap-zip", (req, res) => {
const files: Array<{ pdfPath: string; filename: string }> = req.body.files || [];
if (!files.length) {
res.status(400).json({ error: "Aucun fichier spécifié" });
return;
}
// Vérifier que tous les chemins sont dans storage/
const resolvedFiles: Array<{ absPath: string; filename: string }> = [];
for (const f of files) {
const normalized = path.normalize(f.pdfPath).replace(/^\/+/, '');
if (normalized.startsWith('..') || !normalized.startsWith('storage')) continue;
const absPath = path.resolve(normalized);
if (fs.existsSync(absPath)) {
resolvedFiles.push({ absPath, filename: f.filename });
}
}
if (!resolvedFiles.length) {
res.status(404).json({ error: "Aucun fichier trouvé" });
return;
}
const zipFilename = `BAP_export_${new Date().toLocaleDateString('fr-CA')}.zip`;
const encodedZip = encodeURIComponent(zipFilename);
res.setHeader("Content-Type", "application/zip");
res.setHeader("Content-Disposition", `attachment; filename="${encodedZip}"; filename*=UTF-8''${encodedZip}`);
const archive = archiver('zip', { zlib: { level: 6 } });
archive.on('error', (err) => { console.error('ZIP error:', err); res.destroy(); });
archive.pipe(res);
// Gérer les doublons de noms de fichiers
const usedNames = new Map<string, number>();
for (const { absPath, filename } of resolvedFiles) {
const base = filename.replace(/\.pdf$/i, '');
const count = usedNames.get(base) || 0;
usedNames.set(base, count + 1);
const finalName = count === 0 ? filename : `${base} (${count}).pdf`;
archive.file(absPath, { name: finalName });
}
archive.finalize();
});
// tRPC API
app.use(
"/api/trpc",