From f9d6912b4a415edef43277eb3e53795ec18b33f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 23 Sep 2019 22:30:17 -0400 Subject: [PATCH] refactor: async/await file --- src/admin/search.js | 3 +- src/batch.js | 17 ++-- src/coverPhoto.js | 8 +- src/events.js | 2 +- src/file.js | 225 +++++++++++++------------------------------- src/meta/js.js | 2 +- test/file.js | 14 ++- 7 files changed, 94 insertions(+), 177 deletions(-) diff --git a/src/admin/search.js b/src/admin/search.js index 6415e98c44..ebb6d9db4a 100644 --- a/src/admin/search.js +++ b/src/admin/search.js @@ -12,7 +12,8 @@ const Translator = require('../translator').Translator; function filterDirectories(directories) { return directories.map(function (dir) { // get the relative path - return dir.replace(/^.*(admin.*?).tpl$/, '$1'); + // convert dir to use forward slashes + return dir.replace(/^.*(admin.*?).tpl$/, '$1').split(path.sep).join('/'); }).filter(function (dir) { // exclude .js files // exclude partials diff --git a/src/batch.js b/src/batch.js index a82582a39c..58ad6d2aba 100644 --- a/src/batch.js +++ b/src/batch.js @@ -3,10 +3,10 @@ const util = require('util'); -var db = require('./database'); -var utils = require('./utils'); +const db = require('./database'); +const utils = require('./utils'); -var DEFAULT_BATCH_SIZE = 100; +const DEFAULT_BATCH_SIZE = 100; const sleep = util.promisify(setTimeout); @@ -32,8 +32,8 @@ exports.processSortedSet = async function (setKey, process, options) { // custom done condition options.doneIf = typeof options.doneIf === 'function' ? options.doneIf : function () {}; - var start = 0; - var stop = options.batch; + let start = 0; + let stop = options.batch; if (process && process.constructor && process.constructor.name !== 'AsyncFunction') { process = util.promisify(process); @@ -66,14 +66,14 @@ exports.processArray = async function (array, process, options) { throw new Error('[[error:process-not-a-function]]'); } - var batch = options.batch || DEFAULT_BATCH_SIZE; - var start = 0; + const batch = options.batch || DEFAULT_BATCH_SIZE; + let start = 0; if (process && process.constructor && process.constructor.name !== 'AsyncFunction') { process = util.promisify(process); } while (true) { - var currentBatch = array.slice(start, start + batch); + const currentBatch = array.slice(start, start + batch); if (!currentBatch.length) { return; @@ -88,4 +88,5 @@ exports.processArray = async function (array, process, options) { } } }; + require('./promisify')(exports); diff --git a/src/coverPhoto.js b/src/coverPhoto.js index ab139393e0..ddc4bc0578 100644 --- a/src/coverPhoto.js +++ b/src/coverPhoto.js @@ -1,10 +1,10 @@ 'use strict'; -var nconf = require('nconf'); -var meta = require('./meta'); +const nconf = require('nconf'); +const meta = require('./meta'); -var coverPhoto = module.exports; +const coverPhoto = module.exports; coverPhoto.getDefaultGroupCover = function (groupName) { return getCover('groups', groupName); @@ -17,7 +17,7 @@ coverPhoto.getDefaultProfileCover = function (uid) { function getCover(type, id) { const defaultCover = nconf.get('relative_path') + '/assets/images/cover-default.png'; if (meta.config[type + ':defaultCovers']) { - var covers = String(meta.config[type + ':defaultCovers']).trim().split(/[\s,]+/g); + const covers = String(meta.config[type + ':defaultCovers']).trim().split(/[\s,]+/g); let coverPhoto = defaultCover; if (!covers.length) { return coverPhoto; diff --git a/src/events.js b/src/events.js index a9babcc5e3..2a05ede75c 100644 --- a/src/events.js +++ b/src/events.js @@ -99,7 +99,7 @@ events.getEvents = async function (filter, start, stop, from, to) { event[key] = validator.escape(String(event[key] || '')); } }); - var e = utils.merge(event); + const e = utils.merge(event); e.eid = undefined; e.uid = undefined; e.type = undefined; diff --git a/src/file.js b/src/file.js index 8641fa026f..19b8fd94c9 100644 --- a/src/file.js +++ b/src/file.js @@ -1,101 +1,54 @@ 'use strict'; -var fs = require('fs'); -var nconf = require('nconf'); -var path = require('path'); -var winston = require('winston'); -var mkdirp = require('mkdirp'); -var mime = require('mime'); -var graceful = require('graceful-fs'); - -var utils = require('./utils'); +const fs = require('fs'); +const nconf = require('nconf'); +const path = require('path'); +const winston = require('winston'); +const mkdirp = require('mkdirp'); +const mime = require('mime'); +const graceful = require('graceful-fs'); +const util = require('util'); + +const readdirAsync = util.promisify(fs.readdir); +const mkdirpAsync = util.promisify(mkdirp); +const copyFileAsync = util.promisify(fs.copyFile); +const writeFleAsync = util.promisify(fs.writeFile); +const statAsync = util.promisify(fs.stat); +const unlinkAsync = util.promisify(fs.unlink); +const linkAsync = util.promisify(fs.link); +const symlinkAsync = util.promisify(fs.symlink); + +const utils = require('./utils'); graceful.gracefulify(fs); -var file = module.exports; - -/** - * Asynchronously copies `src` to `dest` - * @param {string} src - source filename to copy - * @param {string} dest - destination filename of the copy operation - * @param {function(Error): void} callback - */ -function copyFile(src, dest, callback) { - var calledBack = false; - - var read; - var write; +const file = module.exports; - function done(err) { - if (calledBack) { - return; - } - calledBack = true; - - if (err) { - if (read) { - read.destroy(); - } - if (write) { - write.destroy(); - } - } - - callback(err); - } - - read = fs.createReadStream(src); - read.on('error', done); - - write = fs.createWriteStream(dest); - write.on('error', done); - write.on('close', function () { - done(); - }); - - read.pipe(write); -} - -file.copyFile = (typeof fs.copyFile === 'function') ? fs.copyFile : copyFile; - -file.saveFileToLocal = function (filename, folder, tempPath, callback) { +file.saveFileToLocal = async function (filename, folder, tempPath) { /* * remarkable doesn't allow spaces in hyperlinks, once that's fixed, remove this. */ - filename = filename.split('.').map(function (name) { - return utils.slugify(name); - }).join('.'); + filename = filename.split('.').map(name => utils.slugify(name)).join('.'); - var uploadPath = path.join(nconf.get('upload_path'), folder, filename); + const uploadPath = path.join(nconf.get('upload_path'), folder, filename); winston.verbose('Saving file ' + filename + ' to : ' + uploadPath); - mkdirp(path.dirname(uploadPath), function (err) { - if (err) { - return callback(err); - } - - file.copyFile(tempPath, uploadPath, function (err) { - if (err) { - return callback(err); - } - - callback(null, { - url: '/assets/uploads/' + (folder ? folder + '/' : '') + filename, - path: uploadPath, - }); - }); - }); + await mkdirpAsync(path.dirname(uploadPath)); + await copyFileAsync(tempPath, uploadPath); + return { + url: '/assets/uploads/' + (folder ? folder + '/' : '') + filename, + path: uploadPath, + }; }; -file.base64ToLocal = function (imageData, uploadPath, callback) { - var buffer = Buffer.from(imageData.slice(imageData.indexOf('base64') + 7), 'base64'); +file.base64ToLocal = async function (imageData, uploadPath) { + const buffer = Buffer.from(imageData.slice(imageData.indexOf('base64') + 7), 'base64'); uploadPath = path.join(nconf.get('upload_path'), uploadPath); - fs.writeFile(uploadPath, buffer, { + await writeFleAsync(uploadPath, buffer, { encoding: 'base64', - }, function (err) { - callback(err, uploadPath); }); + return uploadPath; }; file.isFileTypeAllowed = async function (path) { @@ -111,7 +64,7 @@ file.isFileTypeAllowed = async function (path) { // https://stackoverflow.com/a/31205878/583363 file.appendToFileName = function (filename, string) { - var dotIndex = filename.lastIndexOf('.'); + const dotIndex = filename.lastIndexOf('.'); if (dotIndex === -1) { return filename + string; } @@ -119,8 +72,8 @@ file.appendToFileName = function (filename, string) { }; file.allowedExtensions = function () { - var meta = require('./meta'); - var allowedExtensions = (meta.config.allowedFileExtensions || '').trim(); + const meta = require('./meta'); + let allowedExtensions = (meta.config.allowedFileExtensions || '').trim(); if (!allowedExtensions) { return []; } @@ -140,16 +93,16 @@ file.allowedExtensions = function () { return allowedExtensions; }; -file.exists = function (path, callback) { - fs.stat(path, function (err) { - if (err) { - if (err.code === 'ENOENT') { - return callback(null, false); - } - return callback(err); +file.exists = async function (path) { + try { + await statAsync(path); + } catch (err) { + if (err.code === 'ENOENT') { + return false; } - callback(null, true); - }); + throw err; + } + return true; }; file.existsSync = function (path) { @@ -165,52 +118,40 @@ file.existsSync = function (path) { return true; }; -file.delete = function (path, callback) { - callback = callback || function () {}; +file.delete = async function (path) { if (!path) { - return setImmediate(callback); + return; } - fs.unlink(path, function (err) { - if (err) { - winston.warn(err); - } - callback(); - }); -}; - -file.link = function link(filePath, destPath, relative, callback) { - if (!callback) { - callback = relative; - relative = false; + try { + await unlinkAsync(path); + } catch (err) { + winston.warn(err); } +}; +file.link = async function link(filePath, destPath, relative) { if (relative && process.platform !== 'win32') { filePath = path.relative(path.dirname(destPath), filePath); } if (process.platform === 'win32') { - fs.link(filePath, destPath, callback); + await linkAsync(filePath, destPath); } else { - fs.symlink(filePath, destPath, 'file', callback); + await symlinkAsync(filePath, destPath, 'file'); } }; -file.linkDirs = function linkDirs(sourceDir, destDir, relative, callback) { - if (!callback) { - callback = relative; - relative = false; - } - +file.linkDirs = async function linkDirs(sourceDir, destDir, relative) { if (relative && process.platform !== 'win32') { sourceDir = path.relative(path.dirname(destDir), sourceDir); } - var type = (process.platform === 'win32') ? 'junction' : 'dir'; - fs.symlink(sourceDir, destDir, type, callback); + const type = (process.platform === 'win32') ? 'junction' : 'dir'; + await symlinkAsync(sourceDir, destDir, type); }; file.typeToExtension = function (type) { - var extension; + let extension = ''; if (type) { extension = '.' + mime.getExtension(type); } @@ -218,46 +159,14 @@ file.typeToExtension = function (type) { }; // Adapted from http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search -file.walk = function (dir, callback) { - var results = []; - - fs.readdir(dir, function (err, list) { - if (err) { - return callback(err); - } - var pending = list.length; - if (!pending) { - return callback(null, results); - } - list.forEach(function (filename) { - filename = dir + '/' + filename; - fs.stat(filename, function (err, stat) { - if (err) { - return callback(err); - } - - if (stat && stat.isDirectory()) { - file.walk(filename, function (err, res) { - if (err) { - return callback(err); - } - - results = results.concat(res); - pending -= 1; - if (!pending) { - callback(null, results); - } - }); - } else { - results.push(filename); - pending -= 1; - if (!pending) { - callback(null, results); - } - } - }); - }); - }); +file.walk = async function (dir) { + const subdirs = await readdirAsync(dir); + const files = await Promise.all(subdirs.map(async (subdir) => { + const res = path.resolve(dir, subdir); + return (await statAsync(res)).isDirectory() ? file.walk(res) : res; + })); + var result = files.reduce((a, f) => a.concat(f), []); + return result; }; require('./promisify')(file); diff --git a/src/meta/js.js b/src/meta/js.js index dd6ab20e6e..f63799404d 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -102,7 +102,7 @@ JS.scripts = { function linkIfLinux(srcPath, destPath, next) { if (process.platform === 'win32') { - file.copyFile(srcPath, destPath, next); + fs.copyFile(srcPath, destPath, next); } else { file.link(srcPath, destPath, true, next); } diff --git a/test/file.js b/test/file.js index 423a08ca39..759bc55ac5 100644 --- a/test/file.js +++ b/test/file.js @@ -22,7 +22,7 @@ describe('file', function () { describe('copyFile', function () { it('should copy a file', function (done) { - file.copyFile(tempPath, uploadPath, function (err) { + fs.copyFile(tempPath, uploadPath, function (err) { assert.ifError(err); assert(file.existsSync(uploadPath)); @@ -38,7 +38,7 @@ describe('file', function () { it('should override an existing file', function (done) { fs.writeFileSync(uploadPath, 'hsdkjhgkjsfhkgj'); - file.copyFile(tempPath, uploadPath, function (err) { + fs.copyFile(tempPath, uploadPath, function (err) { assert.ifError(err); assert(file.existsSync(uploadPath)); @@ -52,7 +52,7 @@ describe('file', function () { }); it('should error if source file does not exist', function (done) { - file.copyFile(tempPath + '0000000000', uploadPath, function (err) { + fs.copyFile(tempPath + '0000000000', uploadPath, function (err) { assert(err); assert.strictEqual(err.code, 'ENOENT'); @@ -64,7 +64,7 @@ describe('file', function () { fs.writeFileSync(uploadPath, 'hsdkjhgkjsfhkgj'); fs.chmodSync(uploadPath, '444'); - file.copyFile(tempPath, uploadPath, function (err) { + fs.copyFile(tempPath, uploadPath, function (err) { assert(err); assert(err.code === 'EPERM' || err.code === 'EACCES'); @@ -105,4 +105,10 @@ describe('file', function () { done(); }); }); + + it('should convert mime type to extension', function (done) { + assert.equal(file.typeToExtension('image/png'), '.png'); + assert.equal(file.typeToExtension(''), ''); + done(); + }); });