batch-delete
Version:
Command line utility to batch delete folders from Github, Dropbox or your file system.
281 lines (265 loc) • 7.21 kB
JavaScript
;
// node modules
const Unifile = require('unifile');
const WebDavConnector = require('unifile-webdav');
const express = require('express')
const launcher = require('browser-launcher3');
const Path = require('path');
const fs = require('fs');
// config
const port = process.env.PORT || '6805';
const rootUrl = `http://localhost:${port}`;
const [node, script, connectorNameCaseSensitive, rootPathArg='/'] = process.argv;
const connectorName = connectorNameCaseSensitive.toLowerCase();
const rootPath = connectorName === 'fs' ? Path.resolve(rootPathArg) : rootPathArg;
const SETTINGS = {
fs: {
recursive: true,
recursiveStartingAtLevel: 0,
},
ftp: {
recursive: true,
recursiveStartingAtLevel: 0,
},
sftp: {
recursive: true,
recursiveStartingAtLevel: 0,
},
webdav: {
recursive: false,
},
github: {
recursive: false,
recursiveStartingAtLevel: 3,
},
dropbox: {
recursive: false,
},
}
// cli toolkit
const cli = require('cli');
cli.disable('help');
const options = cli.parse();
const test = !!options['test'] || !!options['t'];
if(test) console.info('\nTEST MODE\n')
// init unifile
const unifile = new Unifile();
const session = {};
const connector = (() => {
switch(connectorName) {
case 'fs':
return new Unifile.FsConnector({
showHiddenFile: true,
sandbox: '/', //Os.homedir(),
infos: {
displayName: 'File System',
},
});
case 'ftp':
return new Unifile.FtpConnector({
redirectUri: rootUrl + '/ftp/signin',
});
case 'sftp':
return new Unifile.SftpConnector({
redirectUri: rootUrl + '/sftp/signin',
});
case 'github':
return new Unifile.GitHubConnector({
clientId: process.env.GITHUB_APP_ID || '9470507937d6b04a70bd',
clientSecret: process.env.GITHUB_APP_SECRET || '106624aedeab491b5f31b361a5d63246250fa8c1',
state: 'aaathub',
redirectUri: process.env.GITHUB_APP_REDIRECT || rootUrl + '/github/oauth_callback',
});
case 'dropbox':
return new Unifile.DropboxConnector({
clientId: process.env.DROPBOX_APP_ID || '8lxz0i3aeztt0im',
clientSecret: process.env.DROBOX_APP_SECRET || 'twhvu6ztqnefkh6',
state: 'aaathub',
redirectUri: process.env.DROPBOX_APP_REDIRECT || rootUrl + '/dropbox/oauth_callback',
});
case 'webdav':
return new WebDavConnector({
redirectUri: rootUrl + '/webdav/signin',
});
default:
console.error('Connector not found:' + connectorName);
process.exit(1);
}
})();
unifile.use(connector);
// authentication
const savedSession = fs.existsSync('.cloud-analyser') ? JSON.parse(fs.readFileSync('.cloud-analyser')) : null;
if(savedSession && savedSession[connectorName]) {
// console.log('> Got session from file .cloud-analyser');
Object.assign(session, savedSession);
ls(rootPath)
.then(root => {
const actions = getTree(root);
if(test) return;
return unifile.batch(session, connectorName, actions);
})
.then(() => {
console.log('Done.')
})
}
else {
unifile.getAuthorizeURL(session, connectorName)
.catch((err) => {
throw('Error while authorizing Unifile: ' + err);
})
.then(result => {
if(!result) return Promise.resolve();
return new Promise((resolve, reject) => {
launcher((err, launch) => {
if ( err ) {
console.error('Can not open browser for auth', err);
reject(err);
}
launch(result, process.env.BROWSER || 'firefox', (err, instance) => {
if(err) {
console.error('Can not open auth URL', err);
reject(err);
}
instance.on( 'stop', function( code ) {
});
waitForToken()
.then(() => {
instance.stop();
resolve();
})
.catch(e => {
instance.stop();
reject(e);
});
});
});
})
})
.catch((err) => {
console.error('Error while authorizing Unifile:', err);
process.exit(1);
})
.then((res) => {
// console.log('> Saving session to .cloud-analyser');
fs.writeFileSync('.cloud-analyser', JSON.stringify(session));
return ls(rootPath);
})
.then(root => {
const actions = getTree(root);
if(test) return;
return unifile.batch(session, connectorName, actions, 'Delete with batch-delete utility');
})
.then(() => {
console.log('Done.')
})
}
function ls(path) {
return unifile.readdir(session, connectorName, path)
.then(files => {
const info = files.reduce(
(prev, file) => {
return {
items: prev.items + 1,
size: isNaN(file.size) ? prev.size : prev.size + file.size
}
},
{items: 0, size: 0}
);
const promises = files
.filter(file => file.isDir)
.map(dir => ls(path + '/' + dir.name));
return Promise.all(promises).then(children => {
const childrenInfo = children.reduce(
(sum, child) => {
return {
size: sum.size + child.size,
items: sum.items + child.items
}
},
{size: 0, items: 0}
);
return {
path: path,
items: info.items + childrenInfo.items,
size: info.size + childrenInfo.size,
folders: children,
files: files.filter(f => !f.isDir)
};
});
});
}
// async function doDelete(path, isDir) {
// console.warn(isDir ? 'd' : 'f', '\t', path);
// if(test) return;
// if(isDir) return unifile.rmdir(session, connectorName, path)
// else return unifile.unlink(session, connectorName, path)
// }
function toAction(path, isDir) {
console.warn(isDir ? 'd' : 'f', '\t', path);
return isDir ? {
name: 'rmdir',
path,
} : {
name: 'unlink',
path,
};
}
function getTree(root) {
let tree = [];
if(SETTINGS[connectorName].recursive &&
root.path.split('/')
.filter(file => !!file)
.length >= SETTINGS[connectorName].recursiveStartingAtLevel) {
tree = tree.concat(
...root.folders
.map(child => getTree(child))
);
tree = tree.concat(
root.files
.map(file => toAction(root.path + '/' + file.name, false))
);
}
return tree.concat(toAction(root.path, root.isDir !== false));
}
function waitForToken() {
let server = null;
return new Promise((resolve, reject) => {
const app = express()
server = app.listen(port, () => {
// register callback url
app.get('/:connector/oauth_callback', (req, res) => {
if('error' in req.query) {
res.status(500).send(req.query);
reject(req.query);
} else {
unifile.login(session, req.params.connector, req.query)
.then((result) => {
if(result) {
res.cookie('unifile_' + req.params.connector, result);
res.end('<script>window.close();</script>');
resolve();
}
else {
reject('auth failed');
}
})
.catch((err) => {
console.error('ERROR', err);
res.status(500).send(err);
reject(err);
});
}
});
app.get('/:connector/signin', (req, res) => {
res.sendFile(Path.join(__dirname, '..', 'oauth_callbacks', req.params.connector + '_login.html'));
});
})
}).then(() => {
server.close();
}).catch(e => {
console.error('stop server, login failed');
server.close();
return Promise.reject(e);
});
}