diff --git a/app/controllers/report.controller.js b/app/controllers/report.controller.js index 886d6e3..928b27b 100644 --- a/app/controllers/report.controller.js +++ b/app/controllers/report.controller.js @@ -2,6 +2,7 @@ const { splitSite } = require("../middlewares"); const db = require("../models"); const fs = require("fs"); const path = require("path"); +const crypto = require("crypto"); const moment = require("moment-timezone"); const archiver = require("archiver"); const { PDFDocument } = require("pdf-lib"); @@ -343,8 +344,7 @@ exports.exportRouteReportZip = async (req, res) => { const templateBytes = fs.readFileSync(templatePath); const unicodeFontBytes = fs.readFileSync(unicodeFontPath); - const filenameDate = (date || "").replace(/\//g, "-"); - const zipName = `route_report_${filenameDate || "date"}.zip`; + const zipName = `${crypto.randomBytes(8).toString("hex")}.zip`; res.setHeader("Content-Type", "application/zip"); res.setHeader("Content-Disposition", `attachment; filename="${zipName}"`); diff --git a/client/src/components/trans-routes/RoutesDashboard.js b/client/src/components/trans-routes/RoutesDashboard.js index 3246d13..da21b10 100644 --- a/client/src/components/trans-routes/RoutesDashboard.js +++ b/client/src/components/trans-routes/RoutesDashboard.js @@ -25,6 +25,18 @@ const RoutesDashboard = () => { return fallback; } }; + const getZipFilenameFromHeaders = (headers, fallback = `route_report_${Date.now()}.zip`) => { + const rawHeader = headers?.['content-disposition'] || headers?.['Content-Disposition'] || ''; + const match = rawHeader.match(/filename\*?=(?:UTF-8'')?\"?([^\";]+)\"?/i); + if (match?.[1]) { + try { + return decodeURIComponent(match[1]); + } catch (_err) { + return match[1]; + } + } + return fallback; + }; const HIDDEN_CUSTOMER_TYPE_FILTER_VALUES = ['transferred', 'deceased', 'discharged']; const navigate = useNavigate(); const dispatch = useDispatch(); @@ -919,10 +931,11 @@ const RoutesDashboard = () => { const selectedDateText = getDateString(targetDate); const response = await ReportService.exportRouteReportZip(selectedDateText); const blob = new Blob([response.data], { type: 'application/zip' }); + const downloadName = getZipFilenameFromHeaders(response?.headers); const downloadUrl = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = downloadUrl; - link.setAttribute('download', `route_report_${selectedDateText.replace(/\//g, '-')}.zip`); + link.setAttribute('download', downloadName); document.body.appendChild(link); link.click(); link.remove(); diff --git a/client/src/components/trans-routes/RoutesHistory.js b/client/src/components/trans-routes/RoutesHistory.js index 69e0bf0..fa12730 100644 --- a/client/src/components/trans-routes/RoutesHistory.js +++ b/client/src/components/trans-routes/RoutesHistory.js @@ -20,6 +20,18 @@ const RoutesHistory = () => { return fallback; } }; + const getZipFilenameFromHeaders = (headers, fallback = `route_report_${Date.now()}.zip`) => { + const rawHeader = headers?.['content-disposition'] || headers?.['Content-Disposition'] || ''; + const match = rawHeader.match(/filename\*?=(?:UTF-8'')?\"?([^\";]+)\"?/i); + if (match?.[1]) { + try { + return decodeURIComponent(match[1]); + } catch (_err) { + return match[1]; + } + } + return fallback; + }; const navigate = useNavigate(); const dispatch = useDispatch(); const inboundRoutes = useSelector(selectHistoryInboundRoutes); @@ -85,10 +97,11 @@ const RoutesHistory = () => { const selectedDateText = getDateString(selectedDate); const response = await ReportService.exportRouteReportZip(selectedDateText); const blob = new Blob([response.data], { type: 'application/zip' }); + const downloadName = getZipFilenameFromHeaders(response?.headers); const downloadUrl = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = downloadUrl; - link.setAttribute('download', `route_report_${selectedDateText.replace(/\//g, '-')}.zip`); + link.setAttribute('download', downloadName); document.body.appendChild(link); link.click(); link.remove();