#!/usr/bin/env node /** * Dependencies */ var program = require('commander') , dpd = require('../') , fs = require('fs') ; // Options program .version('0.4.0') .option('-j, --json', 'output as json') .option('-p, --port ', 'specify the port (2403)') .option('-s, --storage ', 'specify where to store resources (mongodb://localhost/deployd)') .option('-d, --destination ', 'specify where to push to when cloning') ; /** * A method to output json. */ function json(obj) { if(program && program.json) process.stdout.write(JSON.stringify(obj)); } /** * Silent if in json mode. */ function log() { if(!program.json) console.log.apply(console, arguments); } /** * List all administration keys (users). */ program .command('keys') .description(' - list all admin keys') .action(function () { var port = program.port || 2403; if(program.storage) { dpd.storage(program.storage); } dpd.use('http://localhost:' + port).use('/keys').get(function (err, keys) { json(keys); log(keys || 'no keys found! see dpd --help'); process.exit(); }) }) /** * Add a new admin key. */ program .command('addkey [key]') .description(" - add a new admin key eg. dpd addkey '{\"user\": \"foo\", \"label\": \"admin\"}'") .action(function (key) { try { key = JSON.parse(key); } catch(e) { key = undefined; } var strength = Math.floor(Math.random() * 5) + 2; while(strength--) { key[keygen()] = keygen() + keygen() + keygen(); } if(typeof key == 'object') { if(program.storage) { dpd.storage(program.storage); } var port = program.port || 2403; dpd.use('http://localhost:' + port).use('/keys').post(key, function (err, newKey) { if(err) { json(err); log('Could not add key', err); } else { log('added key', newKey); } process.exit(); }) } else { var msg = 'key must be a valid JSON object with more than one attribute!'; json(new Error(msg)) log(msg); } }) ; /** * Keygen */ function keygen() { // TODO added randomness return Math.random().toString().split('.')[1]; } /** * Generate a key */ program .command('key') .description(" - generate, add, and print a random admin key") .action(function () { var port = program.port || 2403; var key = {}, strength = Math.floor(Math.random() * 5) + 2; while(strength--) { key[keygen()] = keygen() + keygen() + keygen(); } if(program.storage) { dpd.storage(program.storage); } dpd.use('http://localhost:' + port).use('/keys').post(key, function (err, newKey) { if(err) { json(err); log('Could not add key', err); } else { json(newKey); log('added key:\n\n' + JSON.stringify(newKey) + '\n'); } process.exit(); }) }) ; /** * Remove an admin key by id. */ program .command('rm [query]') .description(' - remove an admin key by query') .action(function (query) { if(!query) return console.log('you must provide the _id of they key you want to remove'); // parse the query query = JSON.parse(query); if(program.storage) { dpd.storage(program.storage); } if(Object.keys(query) < 1) throw 'use `dpd reset` to clear all keys'; var port = program.port || 2403; dpd.use('http://localhost:' + port).use('/keys').del(query, function (err) { if(err) { json(err); log(err); } else { json({removed: query}); log('removed all keys matching the given query'); } process.exit(); }) }); /** * Remove all admin keys. */ program .command('reset') .description(" - remove all admin keys by query (cannot be undone)'") .action(function () { if(program.storage) { dpd.storage(program.storage); } var port = program.port || 2403; dpd.use('http://localhost:' + port).use('/keys').del(function (err) { if(err) { json(err); log(err); } else { json({removed: 'all'}); log('removed all keys'); } process.exit(); }) }); /** * Start the http server and listen. */ program .command('listen') .description(' - start listening over http at the provided port') .action(function (cmd) { var port = program.port || 2403; if(program.storage) { dpd.storage(program.storage); } dpd.use('http://localhost:' + port).listen(function () { json({port: port, storage: dpd.storage()}); log('deployd is listening on port %s and storing resources at %s', port, dpd.storage()); }); }) ; /** * Clone one dpd configuration into another. */ program .command('clone') .description(' - clone the configuration and files of one instance into another') .action(function () { var port = program.port || 2403; if(!program.storage || !program.destination) throw 'a destination (--dest) and storage (--storage) are required'; dpd.storage(program.storage); dpd.use('/resources').get(function (err, resources) { if(!resources) { json({status: 'none found'}); log('none found'); process.kill(process.pid); return; }; if(err) throw err; var dest = dpd.use('/resources'); dest.storage(program.destination); dest.post(resources, function (err) { if(err) throw err; var remaining = 0; resources.forEach(function (resource) { if(resource.type === 'Static') { dpd.storage(program.storage); // / is an alias for /index var collection = resource.path; if(collection === '/') collection = '/index'; var srcFiles = require('mdoq').require('mdoq-mongodb').use(program.storage).use(collection); var destFiles = require('mdoq').require('mdoq-mongodb').use(program.destination).use(collection); dpd.use(resource.path).get({}, function (err, files) { files.forEach(function (file) { remaining++; var id = {_id: file}; srcFiles.get(id, function (err, res) { if(err) log(err); if(!err && res) { var temp = require('fs').createWriteStream('/tmp/' + file); res.file.stream(true).pipe(temp).on('close', function () { destFiles.get({}).file(require('fs').createReadStream('/tmp/' + file)).post(function (err) { remaining--; if(!remaining) { json({status: 'done.'}); log('done.'); process.kill(process.pid); } }) }) } }) }) }) } }) }) }) }) ; program .command('init [remote] [key]') .description(' - initialize a set of local files') .action(function (remote, key) { if(!remote) { log('A remote host is required'); } if(!key) { log('A valid JSON auth key is required'); } if(!key || !remote) { log('example:'); log('\tdpd init http://localhost:2403 \'{"foo":"bar"}\''); return; } try { JSON.parse(key); } catch(e) { log('invalid JSON for key, make sure it is a string: \'{"foo": "bar"}\''); return; } var config = { key: key, remote: remote }; fs.writeFileSync('./.dpd', JSON.stringify(config)); }) ; program .command('pull') .description(' - download a remote instance configuration and files into the current directory') .action(function () { try { var config = JSON.parse(fs.readFileSync('./.dpd')); } catch(e) { log('you must run dpd init before pull'); } var key = config.key , remote = config.remote; var client = require('mdoq') .use(function (req, res, next) { req.headers['x-dssh-key'] = key; next(); }) .require('mdoq-http') .use(remote) ; var remaining = 0; client .use('/resources').get(function (err, resources) { if(err || resources && resources.status) return log(err || resources); if(!resources) { log('done.'); process.kill(process.pid); return; } resources.forEach(function (resource) { if(resource.type === 'Static') { client.use(resource.path).get(function (err, files) { if(resource.path === '/') resource.path = ''; if(files) { try { fs.mkdirSync('.' + resource.path); } catch(e) {} files.forEach(function (file) { remaining++; log('PULL', resource.path + '/' + file); client.use(resource.path + '/' + file) .get() .pipe(fs.createWriteStream('.' + resource.path + '/' + file)) .get(function () { remaining--; if(!remaining) { log('done.'); process.kill(process.pid); } }) }) } }) } }) }) }) ; program .command('push') .description(' - push the current directories first level of files to the remote instance') .action(function () { try { var config = JSON.parse(fs.readFileSync('./.dpd')); } catch(e) { log('you must run dpd init before push'); return; } var key = config.key , remote = config.remote; var client = require('mdoq') .use(function (req, res, next) { req.headers['x-dssh-key'] = key; next(); }) .require('mdoq-http') .use(remote) ; client .use('/resources').get(function (err, resources) { if(err || resources && resources.status) return log(err || resources); var resourceMap = {}; // create resource map resources && resources.forEach(function (resource) { if(resource.type === 'Static') { resourceMap[resource.path] = true; } }) function uploadDir(path) { if(path === '/') path = ''; fs.readdirSync('.' + path).forEach(function (file) { if(fs.statSync('.' + path + '/' + file).isFile()) { uploadFile(path + '/' + file); } }) } var remaining = 0; function uploadFile(path) { if(path[0] === '.') return; // skip hidden files remaining++; console.log('PUSH', path); client.use(path).post(fs.createReadStream('.' + path), function (err) { remaining--; if(err) { log(err); return process.kill(); } if(!remaining) { log('done.'); process.kill(process.pid); } }); } var creatingIndex; fs.readdirSync('.').forEach(function (path) { if(path[0] === '.') return; // skip hidden files path = '/' + path; if(fs.statSync('.' + path).isFile() && !creatingIndex) { if(resourceMap['/']) { uploadFile(path); } else { console.log('CREATE /'); creatingIndex = true; // create the resource client.use('/resources').post({path: '/', typeLabel: 'Files', type: 'Static'}, function (err) { // then upload the directory uploadDir('/'); }); } } else { // folder if(resourceMap[path]) { // exists as a resource uploadDir(path); } else { // create the resource console.log('CREATE', path); remaining++; client.use('/resources').post({path: path, typeLabel: 'Files', type: 'Static'}, function (err) { remaining--; // then upload the directory uploadDir(path); }); } } }) }) ; }) ; // Parse program.parse(process.argv);