const unzipper = require('unzipper'); const fs = require('fs-extra'); const path = require('path'); const { pipeline } = require('stream/promises'); const { getDestinationDecision, getCadInfo } = require('./router'); const { buildDestinationIndex } = require('./destinationIndex'); const { buildExistingCadKeyIndex, toCadKey } = require('./duplicateIndex'); const { prepareUnroutedTarget, prepareDuplicateTarget, getSkippedTarget } = require('./unrouted'); function parseNumericVersion(version) { const rawVersion = String(version || '').trim(); if (!rawVersion || !/^\d+$/.test(rawVersion)) { return null; } try { return BigInt(rawVersion); } catch { return null; } } async function buildZipHighestNumericVersionByCadKey(zipPath) { const index = new Map(); let directory; try { directory = await unzipper.Open.file(zipPath); } catch { return index; } for (const row of directory.files || []) { if (row.type !== 'File') { continue; } const baseName = path.basename(row.path || ''); const cadInfo = getCadInfo(baseName); if (!cadInfo) { continue; } const version = parseNumericVersion(cadInfo.version); if (version === null) { continue; } const key = toCadKey(cadInfo); const current = index.get(key); if (current === undefined || version > current) { index.set(key, version); } } return index; } async function processZip(zipPath, config) { const stream = fs.createReadStream(zipPath).pipe(unzipper.Parse({ forceStream: true })); const destinationIndex = await buildDestinationIndex(config?.destination); const existingCadKeys = await buildExistingCadKeyIndex(config?.destination); const sourceMaxVersions = await buildZipHighestNumericVersionByCadKey(zipPath); const result = { scanned: 0, copied: 0, skipped: 0, unrouted: 0, duplicates: 0, details: [], }; for await (const entry of stream) { if (entry.type !== 'File') { entry.autodrain(); continue; } const file = entry.path; const baseName = path.basename(file); result.scanned += 1; const cadInfo = getCadInfo(baseName); if (!cadInfo) { const skippedTarget = await getSkippedTarget(baseName); await pipeline(entry, fs.createWriteStream(skippedTarget.destinationPath)); result.skipped += 1; result.copied += 1; continue; } if (existingCadKeys.has(toCadKey(cadInfo))) { const incomingVersion = parseNumericVersion(cadInfo.version); const highestInSource = sourceMaxVersions.get(toCadKey(cadInfo)); if (incomingVersion !== null && highestInSource !== undefined && incomingVersion < highestInSource) { result.details.push({ file: baseName, reason: 'Versione piu alta presente nei file da smistare', }); entry.autodrain(); continue; } const duplicateTarget = await prepareDuplicateTarget(baseName); if (!duplicateTarget.shouldCopy) { result.details.push({ file: baseName, destination: duplicateTarget.destinationDir, reason: duplicateTarget.reason || 'Versione piu alta gia presente', }); entry.autodrain(); continue; } await pipeline(entry, fs.createWriteStream(duplicateTarget.destinationPath)); result.copied += 1; result.duplicates += 1; result.details.push({ file: baseName, destination: duplicateTarget.destinationDir, reason: 'Duplicato gia presente prima dello smistamento', }); continue; } const decision = getDestinationDecision(baseName, config, destinationIndex); const destDir = decision.destination; if (!destDir) { const unroutedTarget = await prepareUnroutedTarget(baseName); if (!unroutedTarget.shouldCopy) { result.details.push({ file: baseName, destination: unroutedTarget.destinationDir, reason: unroutedTarget.reason || 'Versione piu alta gia presente', }); entry.autodrain(); continue; } await pipeline(entry, fs.createWriteStream(unroutedTarget.destinationPath)); result.copied += 1; result.unrouted += 1; result.details.push({ file: baseName, destination: unroutedTarget.destinationDir, reason: decision.reason || 'Nessuna regola trovata', }); continue; } const dest = path.join(destDir, baseName); await pipeline(entry, fs.createWriteStream(dest)); result.copied += 1; result.details.push({ file: baseName, destination: destDir }); } return result; } module.exports = { processZip };