oputil fb rm|remove|del|delete functionality
This commit is contained in:
parent
b0260049ba
commit
1ad5b125f5
|
@ -11,6 +11,7 @@ const async = require('async');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const paths = require('path');
|
const paths = require('path');
|
||||||
const fse = require('fs-extra');
|
const fse = require('fs-extra');
|
||||||
|
const { unlink } = require('graceful-fs');
|
||||||
|
|
||||||
const FILE_TABLE_MEMBERS = [
|
const FILE_TABLE_MEMBERS = [
|
||||||
'file_id', 'area_tag', 'file_sha256', 'file_name', 'storage_tag',
|
'file_id', 'area_tag', 'file_sha256', 'file_name', 'storage_tag',
|
||||||
|
@ -541,6 +542,40 @@ module.exports = class FileEntry {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static removeEntry(srcFileEntry, options, cb) {
|
||||||
|
if(!_.isFunction(cb) && _.isFunction(options)) {
|
||||||
|
cb = options;
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
function removeFromDatabase(callback) {
|
||||||
|
fileDb.run(
|
||||||
|
`DELETE FROM file
|
||||||
|
WHERE file_id = ?;`,
|
||||||
|
[ srcFileEntry.fileId ],
|
||||||
|
err => {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
function optionallyRemovePhysicalFile(callback) {
|
||||||
|
if(true !== options.removePhysFile) {
|
||||||
|
return callback(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
unlink(srcFileEntry.filePath, err => {
|
||||||
|
return callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static moveEntry(srcFileEntry, destAreaTag, destStorageTag, destFileName, cb) {
|
static moveEntry(srcFileEntry, destAreaTag, destStorageTag, destFileName, cb) {
|
||||||
if(!cb && _.isFunction(destFileName)) {
|
if(!cb && _.isFunction(destFileName)) {
|
||||||
cb = destFileName;
|
cb = destFileName;
|
||||||
|
@ -550,7 +585,6 @@ module.exports = class FileEntry {
|
||||||
const srcPath = srcFileEntry.filePath;
|
const srcPath = srcFileEntry.filePath;
|
||||||
const dstDir = FileEntry.getAreaStorageDirectoryByTag(destStorageTag);
|
const dstDir = FileEntry.getAreaStorageDirectoryByTag(destStorageTag);
|
||||||
|
|
||||||
|
|
||||||
if(!dstDir) {
|
if(!dstDir) {
|
||||||
return cb(Errors.Invalid('Invalid storage tag'));
|
return cb(Errors.Invalid('Invalid storage tag'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -438,6 +438,62 @@ function scanFileAreas() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function expandFileTargets(targets, cb) {
|
||||||
|
let entries = [];
|
||||||
|
|
||||||
|
// Each entry may be PATH|FILE_ID|SHA|AREA_TAG[@STORAGE_TAG]
|
||||||
|
const FileEntry = require('../../core/file_entry.js');
|
||||||
|
|
||||||
|
async.eachSeries(targets, (areaAndStorage, next) => {
|
||||||
|
const areaInfo = fileArea.getFileAreaByTag(areaAndStorage.areaTag);
|
||||||
|
|
||||||
|
if(areaInfo) {
|
||||||
|
// AREA_TAG[@STORAGE_TAG] - all files in area@tag
|
||||||
|
const findFilter = {
|
||||||
|
areaTag : areaAndStorage.areaTag,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(areaAndStorage.storageTag) {
|
||||||
|
findFilter.storageTag = areaAndStorage.storageTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileEntry.findFiles(findFilter, (err, fileIds) => {
|
||||||
|
if(err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
async.each(fileIds, (fileId, nextFileId) => {
|
||||||
|
const fileEntry = new FileEntry();
|
||||||
|
fileEntry.load(fileId, err => {
|
||||||
|
if(!err) {
|
||||||
|
entries.push(fileEntry);
|
||||||
|
}
|
||||||
|
return nextFileId(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
return next(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// FILENAME_WC|FILE_ID|SHA|PARTIAL_SHA
|
||||||
|
// :TODO: FULL_PATH -> entries
|
||||||
|
getFileEntries(areaAndStorage.pattern, (err, fileEntries) => {
|
||||||
|
if(err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
entries = entries.concat(fileEntries);
|
||||||
|
return next(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
return cb(err, entries);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function moveFiles() {
|
function moveFiles() {
|
||||||
//
|
//
|
||||||
// oputil fb move SRC [SRC2 ...] DST
|
// oputil fb move SRC [SRC2 ...] DST
|
||||||
|
@ -450,8 +506,9 @@ function moveFiles() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const moveArgs = argv._.slice(2);
|
const moveArgs = argv._.slice(2);
|
||||||
let src = getAreaAndStorage(moveArgs.slice(0, -1));
|
const src = getAreaAndStorage(moveArgs.slice(0, -1));
|
||||||
let dst = getAreaAndStorage(moveArgs.slice(-1))[0];
|
const dst = getAreaAndStorage(moveArgs.slice(-1))[0];
|
||||||
|
|
||||||
let FileEntry;
|
let FileEntry;
|
||||||
|
|
||||||
async.waterfall(
|
async.waterfall(
|
||||||
|
@ -465,8 +522,6 @@ function moveFiles() {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function validateAndExpandSourceAndDest(callback) {
|
function validateAndExpandSourceAndDest(callback) {
|
||||||
let srcEntries = [];
|
|
||||||
|
|
||||||
const areaInfo = fileArea.getFileAreaByTag(dst.areaTag);
|
const areaInfo = fileArea.getFileAreaByTag(dst.areaTag);
|
||||||
if(areaInfo) {
|
if(areaInfo) {
|
||||||
dst.areaInfo = areaInfo;
|
dst.areaInfo = areaInfo;
|
||||||
|
@ -474,57 +529,9 @@ function moveFiles() {
|
||||||
return callback(Errors.DoesNotExist('Invalid or unknown destination area'));
|
return callback(Errors.DoesNotExist('Invalid or unknown destination area'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each SRC may be PATH|FILE_ID|SHA|AREA_TAG[@STORAGE_TAG]
|
|
||||||
FileEntry = require('../../core/file_entry.js');
|
FileEntry = require('../../core/file_entry.js');
|
||||||
|
|
||||||
async.eachSeries(src, (areaAndStorage, next) => {
|
expandFileTargets(src, (err, srcEntries) => {
|
||||||
const areaInfo = fileArea.getFileAreaByTag(areaAndStorage.areaTag);
|
|
||||||
|
|
||||||
if(areaInfo) {
|
|
||||||
// AREA_TAG[@STORAGE_TAG] - all files in area@tag
|
|
||||||
src.areaInfo = areaInfo;
|
|
||||||
|
|
||||||
const findFilter = {
|
|
||||||
areaTag : areaAndStorage.areaTag,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(areaAndStorage.storageTag) {
|
|
||||||
findFilter.storageTag = areaAndStorage.storageTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileEntry.findFiles(findFilter, (err, fileIds) => {
|
|
||||||
if(err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
async.each(fileIds, (fileId, nextFileId) => {
|
|
||||||
const fileEntry = new FileEntry();
|
|
||||||
fileEntry.load(fileId, err => {
|
|
||||||
if(!err) {
|
|
||||||
srcEntries.push(fileEntry);
|
|
||||||
}
|
|
||||||
return nextFileId(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
err => {
|
|
||||||
return next(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// FILENAME_WC|FILE_ID|SHA|PARTIAL_SHA
|
|
||||||
// :TODO: FULL_PATH -> entries
|
|
||||||
getFileEntries(areaAndStorage.pattern, (err, entries) => {
|
|
||||||
if(err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
srcEntries = srcEntries.concat(entries);
|
|
||||||
return next(null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
err => {
|
|
||||||
return callback(err, srcEntries);
|
return callback(err, srcEntries);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -555,13 +562,80 @@ function moveFiles() {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
err => {
|
||||||
|
if(err) {
|
||||||
|
process.exitCode = ExitCodes.ERROR;
|
||||||
|
console.error(err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFiles() {
|
function removeFiles() {
|
||||||
//
|
//
|
||||||
// REMOVE FILENAME_WC|SHA|FILE_ID [SHA|FILE_ID ...]
|
// oputil fb rm|remove|del|delete SRC [SRC2 ...]
|
||||||
|
//
|
||||||
|
// SRC: FILENAME_WC|FILE_ID|SHA|AREA_TAG[@STORAGE_TAG]
|
||||||
|
//
|
||||||
|
// AREA_TAG[@STORAGE_TAG] remove all entries matching
|
||||||
|
// supplied area/storage tags
|
||||||
|
//
|
||||||
|
// --phys-file removes backing physical file(s)
|
||||||
|
//
|
||||||
|
if(argv._.length < 3) {
|
||||||
|
return printUsageAndSetExitCode(getHelpFor('FileBase'), ExitCodes.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
const removePhysFile = argv['phys-file'];
|
||||||
|
|
||||||
|
const src = getAreaAndStorage(argv._.slice(2));
|
||||||
|
|
||||||
|
async.waterfall(
|
||||||
|
[
|
||||||
|
function init(callback) {
|
||||||
|
return initConfigAndDatabases( err => {
|
||||||
|
if(!err) {
|
||||||
|
fileArea = require('../../core/file_base_area.js');
|
||||||
|
}
|
||||||
|
return callback(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function expandSources(callback) {
|
||||||
|
expandFileTargets(src, (err, srcEntries) => {
|
||||||
|
return callback(err, srcEntries);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function removeEntries(srcEntries, callback) {
|
||||||
|
const FileEntry = require('../../core/file_entry.js');
|
||||||
|
|
||||||
|
const extraOutput = removePhysFile ? ' (including physical file)' : '';
|
||||||
|
|
||||||
|
async.eachSeries(srcEntries, (entry, nextEntry) => {
|
||||||
|
|
||||||
|
process.stdout.write(`Removing ${entry.filePath}${extraOutput}... `);
|
||||||
|
|
||||||
|
FileEntry.removeEntry(entry, { removePhysFile }, err => {
|
||||||
|
if(err) {
|
||||||
|
console.info(`Failed: ${err.message}`);
|
||||||
|
} else {
|
||||||
|
console.info('Done');
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextEntry(err);
|
||||||
|
});
|
||||||
|
}, err => {
|
||||||
|
return callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if(err) {
|
||||||
|
process.exitCode = ExitCodes.ERROR;
|
||||||
|
console.error(err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFileBaseCommand() {
|
function handleFileBaseCommand() {
|
||||||
|
@ -582,7 +656,13 @@ function handleFileBaseCommand() {
|
||||||
return ({
|
return ({
|
||||||
info : displayFileAreaInfo,
|
info : displayFileAreaInfo,
|
||||||
scan : scanFileAreas,
|
scan : scanFileAreas,
|
||||||
|
|
||||||
|
mv : moveFiles,
|
||||||
move : moveFiles,
|
move : moveFiles,
|
||||||
|
|
||||||
|
rm : removeFiles,
|
||||||
remove : removeFiles,
|
remove : removeFiles,
|
||||||
|
del : removeFiles,
|
||||||
|
delete : removeFiles,
|
||||||
}[action] || errUsage)();
|
}[action] || errUsage)();
|
||||||
}
|
}
|
|
@ -45,7 +45,7 @@ import-areas args:
|
||||||
--type TYPE specifies area import type. valid options are "bbs" and "na"
|
--type TYPE specifies area import type. valid options are "bbs" and "na"
|
||||||
`,
|
`,
|
||||||
FileBase :
|
FileBase :
|
||||||
`usage: oputil.js fb <action> [<args>] <AREA_TAG|SHA|FILE_ID[@STORAGE_TAG] ...> [<args>]
|
`usage: oputil.js fb <action> [<args>]
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
scan AREA_TAG[@STORAGE_TAG] scan specified area
|
scan AREA_TAG[@STORAGE_TAG] scan specified area
|
||||||
|
@ -53,14 +53,16 @@ actions:
|
||||||
info AREA_TAG|SHA|FILE_ID display information about areas and/or files
|
info AREA_TAG|SHA|FILE_ID display information about areas and/or files
|
||||||
SHA may be a full or partial SHA-256
|
SHA may be a full or partial SHA-256
|
||||||
|
|
||||||
move SRC [SRC...]] DST move entry(s) from SRC to DST
|
mv SRC [SRC...] DST move entry(s) from SRC to DST
|
||||||
* SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
|
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
|
||||||
* DST: AREA_TAG[@STORAGE_TAG]
|
DST: AREA_TAG[@STORAGE_TAG]
|
||||||
|
|
||||||
remove SHA|FILE_ID removes a entry from the system
|
rm SRC [SRC...] remove entry(s) from the system matching SRC
|
||||||
|
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
|
||||||
|
|
||||||
scan args:
|
scan args:
|
||||||
--tags TAG1,TAG2,... specify tag(s) to assign to discovered entries
|
--tags TAG1,TAG2,... specify tag(s) to assign to discovered entries
|
||||||
|
|
||||||
--desc-file [PATH] prefer file descriptions from DESCRIPT.ION file over
|
--desc-file [PATH] prefer file descriptions from DESCRIPT.ION file over
|
||||||
other sources such as FILE_ID.DIZ.
|
other sources such as FILE_ID.DIZ.
|
||||||
if PATH is specified, use DESCRIPT.ION at PATH instead
|
if PATH is specified, use DESCRIPT.ION at PATH instead
|
||||||
|
@ -71,7 +73,7 @@ info args:
|
||||||
--show-desc display short description, if any
|
--show-desc display short description, if any
|
||||||
|
|
||||||
remove args:
|
remove args:
|
||||||
--delete also remove underlying physical file
|
--phys-file also remove underlying physical file
|
||||||
`,
|
`,
|
||||||
FileOpsInfo :
|
FileOpsInfo :
|
||||||
`
|
`
|
||||||
|
|
Loading…
Reference in New Issue