DockerERTFF/lib/modules/fs.js

258 lines
8.8 KiB
JavaScript

const fs = require('fs-extra');
const { join, dirname, basename, extname } = require('path');
const { toSystemPath, toAppPath, toSiteUrl, getUniqFile, parseTemplate } = require('../core/path');
const { map } = require('../core/async');
module.exports = {
download: async function(options) {
let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.download: path is required.'));
let filename = this.parseOptional(options.filename, 'string', basename(path));
if (fs.existsSync(path)) {
this.res.download(path, filename);
this.noOutput = true;
} else {
this.res.sendStatus(404);
}
},
exists: async function(options) {
let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.exists: path is required.'));
if (await isFile(path)) {
if (options.then) {
await this.exec(options.then, true);
}
return true;
} else {
if (options.else) {
await this.exec(options.else, true);
}
return false;
}
},
direxists: async function(options) {
let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.direxists: path is required.'));
if (await isDirectory(path)) {
if (options.then) {
await this.exec(options.then, true);
}
return true;
} else {
if (options.else) {
await this.exec(options.else, true);
}
return false;
}
},
createdir: async function(options) {
let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.createdir: path is required.'));
await fs.ensureDir(path);
return toAppPath(path);
},
removedir: async function(options) {
let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.removedir: path is required.'));
await fs.remove(path);
return toAppPath(path);
},
emptydir: async function(options) {
let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.emptydir: path is required.'));
await fs.emptyDir(path)
return toAppPath(path);
},
move: async function(options) {
let from = toSystemPath(this.parseRequired(options.from, 'string', 'fs.move: from is required.'));
let to = toSystemPath(this.parseRequired(options.to, 'string', 'fs.move: to is required.'));
let overwrite = this.parseOptional(options.overwrite, 'boolean', false);
let createdir = this.parseOptional(options.createdir, 'boolean', true);
if (!fs.existsSync(to)) {
if (createdir) {
await fs.ensureDir(to);
} else {
throw new Error(`Destination path doesn't exists.`);
}
}
to = join(to, basename(from));
if (!overwrite) {
to = getUniqFile(to);
}
await fs.move(from, to, { overwrite: true });
return toAppPath(to);
},
rename: async function(options) {
let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.rename: path is required.'));
let template = this.parseRequired(options.template, 'string', 'fs.rename: template is required.');
let overwrite = this.parseOptional(options.overwrite, 'boolean', false);
let to = parseTemplate(path, template);
if (!overwrite && fs.existsSync(to)) {
throw new Error(`fs.rename: file "${to}" already exists.`);
}
await fs.rename(path, to);
return toAppPath(to);
},
copy: async function(options) {
let from = toSystemPath(this.parseRequired(options.from, 'string', 'fs.copy: from is required.'));
let to = toSystemPath(this.parseRequired(options.to, 'string', 'fs.copy: to is required.'));
let overwrite = this.parseOptional(options.overwrite, 'boolean', false);
let createdir = this.parseOptional(options.createdir, 'boolean', true);
if (!fs.existsSync(to)) {
if (createdir) {
await fs.ensureDir(to);
} else {
throw new Error(`Destination path doesn't exist.`);
}
}
to = join(to, basename(from));
if (!overwrite && fs.existsSync(to)) {
to = getUniqFile(to);
}
await fs.copy(from, to);
return toAppPath(to);
},
remove: async function(options) {
let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.remove: path is required.'));
await fs.unlink(path);
return true;
},
dir: async function(options) {
let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.dir: path is required.'));
let allowedExtensions = this.parseOptional(options.allowedExtensions, 'string', '');
let showHidden = this.parseOptional(options.showHidden, 'boolean', false);
let includeFolders = this.parseOptional(options.includeFolders, 'boolean', false);
let folderSize = this.parseOptional(options.folderSize, 'string', 'none');
let concurrency = this.parseOptional(options.concurrency, 'number', 4);
folderSize = ['none', 'files', 'recursive'].includes(folderSize) ? folderSize : 'none';
allowedExtensions = allowedExtensions ? allowedExtensions.split(/\s*,\s*/).map(ext => lowercase(ext[0] == '.' ? ext : '.' + ext)) : [];
let files = await fs.readdir(path, { withFileTypes: true });
files = files.filter(entry => {
if (!includeFolders && entry.isDirectory()) return false;
if (!showHidden && entry.name[0] == '.') return false;
if (allowedExtensions.length && entry.isFile() && !allowedExtensions.includes(lowercase(extname(entry.name)))) return false;
return entry.isFile() || entry.isDirectory();
});
// Fast parallel map
return map(files, async (entry) => {
let curr = join(path, entry.name);
let stat = await fs.stat(curr);
if (folderSize != 'none' && entry.isDirectory()) {
stat.size = await calcSize(curr, folderSize == 'recursive', concurrency);
}
return {
type: entry.isFile() ? 'file' : 'dir',
name: entry.name,
folder: toAppPath(dirname(curr)),
basename: basename(curr, extname(curr)),
extension: extname(curr),
path: toAppPath(curr),
url: toSiteUrl(curr),
size: stat.size,
created: stat.ctime,
accessed: stat.atime,
modified: stat.mtime
};
}, concurrency);
},
stat: async function(options) {
let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.stat: path is required.'));
let folderSize = this.parseOptional(options.folderSize, 'string', 'none');
let concurrency = this.parseOptional(options.concurrency, 'number', 4);
folderSize = ['none', 'files', 'recursive'].includes(folderSize) ? folderSize : 'none';
let stat = await fs.stat(path);
if (folderSize != 'none' && stat.isDirectory()) {
stat.size = await calcSize(path, folderSize == 'recursive', concurrency);
}
return {
type: stat.isFile() ? 'file' : 'dir',
name: basename(path),
folder: toAppPath(dirname(path)),
basename: basename(path, extname(path)),
extension: extname(path),
path: toAppPath(path),
url: toSiteUrl(path),
size: stat.size,
created: stat.ctime,
accessed: stat.atime,
modified: stat.mtime
};
},
};
function lowercase(str) {
return str.toLowerCase();
}
async function calcSize(folder, recursive, concurrency) {
let entries = await fs.readdir(folder);
return map(entries, async (entry) => {
let stat = await fs.stat(join(folder, entry));
if (stat.isDirectory() && recursive) {
return calcSize(join(folder, entry), recursive, concurrency);
}
return stat.size;
}, concurrency).then(arr => arr.reduce((size, curr) => size + curr, 0));
};
async function isFile(path) {
try {
let stats = await fs.stat(path);
return stats.isFile();
} catch (err) {
return false;
}
}
async function isDirectory(path) {
try {
let stats = await fs.stat(path);
return stats.isDirectory();
} catch (err) {
return false;
}
}