import readdir from 'readdir-enhanced'; import fs from 'fs'; import crypto from 'crypto'; import multimatch from 'multimatch'; import { cfg } from './repo-config.js'; function applyExcludeFilter(stat, excludeFilters) { // match exclude, return immediatley if (excludeFilters.length > 0) { // todo this could be a performance problem... const pathWithFolderSlash = stat.path + (stat.isDirectory() ? '/' : ''); const excludeMatch = multimatch(pathWithFolderSlash, excludeFilters, { matchBase: true, dot: true }); if (excludeMatch.length > 0) { return false; } } return true; } /** * @param algorithm : "md5" | "sha1" | "sha256" | "sha512" */ async function fileHash(filename, algorithm) { return new Promise((resolve, reject) => { // Algorithm depends on availability of OpenSSL on platform // Another algorithms: "sha1", "md5", "sha256", "sha512" ... let shasum = crypto.createHash(algorithm); try { let s = fs.createReadStream(filename); s.on('data', function (data) { shasum.update(data); }); s.on('error', function (error) { reject(error); }); // making digest s.on('end', function () { const hash = shasum.digest('hex'); return resolve(hash); }); } catch (error) { return reject('calc fail'); } }); } function createLocalState(localFiles, args) { console.log(`Creating local state at ${args['local-dir']}${args['state-name']}`); fs.writeFileSync(`${args['local-dir']}${args['state-name']}`, JSON.stringify(localFiles, undefined, 4), { encoding: 'utf8' }); console.log('Local state created'); } async function getLocalFiles(args) { const files = await readdir.async(args['local-dir'], { deep: true, stats: true, sep: '/', filter: (stat) => applyExcludeFilter(stat, args.exclude) }); let records = []; console.log(files.length, 'items'); for (let stat of files) { if (stat.isDirectory()) { records.push({ type: 'folder', name: stat.path, size: undefined, }); continue; } if (stat.isFile()) { records.push({ type: 'file', name: stat.path, size: stat.size, hash: await fileHash(args['local-dir'] + stat.path, 'sha256'), }); continue; } if (stat.isSymbolicLink()) { console.warn('This script is currently unable to handle symbolic links - please add a feature request if you need this'); } } return { description: "Don't delete. Generate local", version: '1.0.0', generatedTime: new Date().getTime(), generatedTimeString: new Date().toLocaleString(), data: records, }; } try { const [, , repoArg] = process.argv; if (!repoArg) { throw "error: Pls input repo: info | ghh | XX"; } const filename = `.generate-file-hash.json`; const _args = { 'local-dir': `./../${cfg[repoArg].folder}/`, 'state-name': filename, 'exclude': [filename, 'ftp-state/**'].concat(cfg[repoArg].exclude), }; const localFiles = await getLocalFiles(_args); createLocalState(localFiles, _args); } catch (error) { console.log(error); }