cloud-disk-usage
Version:
Command line utility which displays the size of folders (recursively) and files in your cloud storage (e.g. Dropbox and more).
229 lines (214 loc) • 6.12 kB
JavaScript
;
// node modules
const Path = require('path');
// const Os = require('os');
const fs = require('fs');
const Unifile = require('unifile');
const launcher = require('browser-launcher3');
const express = require('express')
const WebDavConnector = require('unifile-webdav');
// 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;
// cli toolkit
const cli = require('cli');
cli.disable('help');
const options = cli.parse();
const optionHumanReadable = !!options['human-readable'] || !!options['h'];
const optionListFiles= !!options['all'] || !!options['a'];
// output format
const humanFormat = require('human-format');
function format(size) {
return optionHumanReadable ? humanFormat(size, {
decimals: 1,
}) : Math.round(size / 1000);
}
// 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(tree => {
displayTree(tree);
})
}
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(tree => {
displayTree(tree);
})
}
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)
};
});
});
}
// linux `du -b` command notation
function displayTree(tree) {
tree.folders.forEach(child => displayTree(child));
if(optionListFiles) {
tree.files.forEach(file => console.info(`${format(file.size)}\t${tree.path}/${file.name}`));
}
console.info(format(tree.size) + '\t' + tree.path);
}
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);
});
}