const unzipper = require('unzipper'); const fs = require('fs-extra'); const path = require('path'); const { pipeline } = require('stream/promises'); const { getDestinationDecision, getCadInfo } = require('./router'); const { buildDestinationIndexes, toCadKey } = require('./destinationScanner'); 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, total: 0 }; } 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, total: (directory.files || []).filter((f) => f.type === 'File').length }; } async function processZip(zipPath, config, onProgress) { onProgress?.({ phase: 'scan' }); const { index: sourceMaxVersions, total } = await buildZipHighestNumericVersionByCadKey(zipPath); const { folderIndex: destinationIndex, cadKeyIndex: existingCadKeys } = await buildDestinationIndexes(config?.destination, onProgress); const stream = fs.createReadStream(zipPath).pipe(unzipper.Parse({ forceStream: true })); const result = { scanned: 0, copied: 0, skipped: 0, unrouted: 0, duplicates: 0, details: [], }; let current = 0; for await (const entry of stream) { if (entry.type !== 'File') { entry.autodrain(); continue; } const file = entry.path; const baseName = path.basename(file); current += 1; result.scanned += 1; onProgress?.({ phase: 'copy', current, total, file: baseName }); 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 };