Skip to content

Commit

Permalink
move some code into functions
Browse files Browse the repository at this point in the history
Signed-off-by: coolguy284 <coolguy284v64@gmail.com>
  • Loading branch information
coolguy284 committed Aug 18, 2022
1 parent 1f0bd3c commit 1088535
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 24 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Hash Backup Tool

This program is used to create backups of a given folder tree by referencing each file by its hash. Each additional backup to the same backup dir will only add new files that have a different hash, so space is conserved. All file timestamps made available in node.js are preserved.

# Usage

Download `hash_backup.js` and place anywhere. Run `node hash_backup.js` to see help.

## Help

```
Node Hash Backup Tool
Usage: node hash_backup.js [command] [arguments]
Command `init`:
Usage: node hash_backup.js init <backupDir>
Initalizes empty hash backup in backup dir.
Command `delete`:
Usage: node hash_backup.js delete <backupDir>
Removes all files at hash backup dir.
```
111 changes: 88 additions & 23 deletions hash_backup.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,54 @@ async function getAllFilesInDir(path, excludeDirs) {
return entries;
}

async function _checkBackupDirIsDir(path) {
let backupDirStats;
try {
backupDirStats = await fs.promises.stat(path);
} catch (e) {
if (e.code != 'ENOENT') throw e;
}

if (backupDirStats == null)
throw new Error(`Error: ${path} does not exist.`);

if (!backupDirStats.isDirectory())
throw new Error(`Error: ${path} not a directory.`);
}

async function initBackupDir(opts) {
if (typeof opts != 'object') opts = {};

let performChecks = typeof opts.performChecks == 'boolean' ? opts.performChecks : true;
let backupDir = typeof opts.backupDir == 'string' && opts.backupDir != '' ? opts.backupDir : '.';

if (performChecks) {
_checkBackupDirIsDir(backupDir);

if ((await fs.promises.readdir(backupDir)).length != 0)
throw new Error(`Error: ${backupDir} already has files in it.`);
}

await fs.promises.mkdir(path.join(backupDir, 'files'));
}

async function deleteBackupDir(opts) {
if (typeof opts != 'object') opts = {};

let performChecks = typeof opts.performChecks == 'boolean' ? opts.performChecks : true;
let backupDir = typeof opts.backupDir == 'string' && opts.backupDir != '' ? opts.backupDir : '.';

if (performChecks)
_checkBackupDirIsDir(backupDir);

let backupDirContents = Array.isArray(opts._backupDirContents) ?
opts._backupDirContents :
await fs.promises.readdir(backupDir, { withFileTypes: true });

for (let backupDirContent of backupDirContents)
await fs.promises.rm(path.join(backupDir, backupDirContent.name), { recursive: true });
}

async function runIfMain() {
let argvSliced = process.argv.slice(2);

Expand All @@ -41,11 +89,11 @@ async function runIfMain() {
'\n' +
'Command `init`:\n' +
' Usage: node hash_backup.js init <backupDir>\n' +
' Initalizes empty hash backup in backup dir\n' +
' Initalizes empty hash backup in backup dir.\n' +
'\n' +
'Command `delete`:\n' +
' Usage: node hash_backup.js delete <backupDir>\n' +
' Removes hash backup at backup dir'
' Removes hash backup at backup dir.'
);
} else {
let commandArgs = argvSliced.slice(1);
Expand All @@ -56,22 +104,7 @@ async function runIfMain() {

if (backupDir == null || backupDir == '') backupDir = '.';

let backupDirStats;
try {
backupDirStats = await fs.promises.stat(backupDir);
} catch (e) {
if (e.code != 'ENOENT') throw e;
}

if (backupDirStats == null) {
console.log(`Error: ${backupDir} does not exist.`);
return;
}

if (!backupDirStats.isDirectory()) {
console.log(`Error: ${backupDir} not a directory.`);
return;
}
_checkBackupDirIsDir(backupDir);

let backupDirContents = await fs.promises.readdir(backupDir, { withFileTypes: true });

Expand All @@ -80,24 +113,56 @@ async function runIfMain() {
`Directory ${backupDir} is not empty, proceed anyway?\n` +
'WARNING: This will remove all files in the directory!'
);

let proceed = await getUserInput();
if (!proceed) return;
if (!proceed) {
console.log('Aborting.');
return;
}

for (let backupDirContent of backupDirContents)
await fs.promises.rm(path.join(backupDir, backupDirContent.name), { recursive: true });
console.log(`Deleting files in ${backupDir}.`);
await deleteBackupDir({ backupDir, performChecks: false, _backupDirContents: backupDirContents });
}

await fs.promises.mkdir(path.join(backupDir, 'files'));
console.log(`Initializing new hash backup in ${backupDir}`);
await initBackupDir({ backupDir, performChecks: false });
break;
}

case 'delete':
case 'delete': {
let backupDir = commandArgs[0];

if (backupDir == null || backupDir == '') backupDir = '.';

_checkBackupDirIsDir(backupDir);

let backupDirContents = await fs.promises.readdir(backupDir, { withFileTypes: true });

if (backupDirContents.length == 0) {
console.log(`Directory ${backupDir} already empty.`);
return;
}

console.log('WARNING: This will remove all files in the directory! Proceed?');

let proceed = await getUserInput();
if (!proceed) {
console.log('Aborting.');
return;
}

console.log(`Deleting files in ${backupDir}.`);
await deleteBackupDir({ backupDir, performChecks: false, _backupDirContents: backupDirContents });
break;
}
}
}
}

module.exports = exports = {
_checkBackupDirIsDir,
getUserInput, getAllFilesInDir,
initBackupDir, deleteBackupDir,
runIfMain,
};

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hash-backup",
"version": "0.0.0a",
"version": "0.0.1a",
"description": "A hash based incremental file backupper.",
"main": "hash_backup.js",
"scripts": {
Expand Down

0 comments on commit 1088535

Please sign in to comment.