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:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user