diff --git a/bin/now-secrets b/bin/now-secrets index a94a5da..0effad1 100755 --- a/bin/now-secrets +++ b/bin/now-secrets @@ -1,8 +1,9 @@ #!/usr/bin/env node import chalk from 'chalk'; +import table from 'text-table'; import minimist from 'minimist'; import * as cfg from '../lib/cfg'; -import { error } from '../lib/error'; +import { handleError, error } from '../lib/error'; import NowSecrets from '../lib/secrets'; import ms from 'ms'; @@ -80,11 +81,7 @@ if (argv.help || !subcommand) { try { await run(token); } catch (err) { - if (err.userError) { - error(err.message); - } else { - error(`Unknown error: ${err.stack}`); - } + handleError(err); exit(1); } }) @@ -104,11 +101,19 @@ async function run (token) { error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret ls`')}`); return exit(1); } - - const list = []; - + const list = await secrets.ls(); const elapsed = ms(new Date() - start); console.log(`> ${list.length} secrets found ${chalk.gray(`[${elapsed}]`)}`); + const cur = Date.now(); + const out = table(list.map((secret) => { + return [ + '', + secret.uid, + chalk.bold(secret.name), + chalk.gray(ms(cur - new Date(secret.created)) + ' ago') + ]; + }), { align: ['l', 'r', 'l'], hsep: ' '.repeat(3) }); + if (out) console.log('\n' + out + '\n'); return secrets.close(); } @@ -117,7 +122,7 @@ async function run (token) { error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rm `')}`); return exit(1); } - + const secret = await secrets.rm(args[0]); const elapsed = ms(new Date() - start); console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(secret.name)} ${chalk.gray(`(${secret.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}`); return secrets.close(); @@ -128,12 +133,7 @@ async function run (token) { error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rename `')}`); return exit(1); } - - const secret = { - name: 'my-password', - uid: 'sec_iuh32u23bfigf2gu' - }; - + const secret = await secrets.patch(args[0], args[1]); const elapsed = ms(new Date() - start); console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(secret.name)} ${chalk.gray(`(${secret.uid})`)} renamed to ${chalk.bold('my-secret-name')} ${chalk.gray(`[${elapsed}]`)}`); return secrets.close(); @@ -149,23 +149,16 @@ async function run (token) { } return exit(1); } - const [name, value_] = args; - let value; if (argv.base64) { value = { base64: value_ }; } else { value = value_; } - - const secret = { - name: 'my-password', - uid: 'sec_iuh32u23bfigf2gu' - }; - + const secret = await secrets.add(name, value); const elapsed = ms(new Date() - start); - console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(secret.name)} ${chalk.gray(`(${secret.uid})`)} added ${chalk.gray(`[${elapsed}]`)}`); + console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(name)} ${chalk.gray(`(${secret.uid})`)} added ${chalk.gray(`[${elapsed}]`)}`); return secrets.close(); } @@ -173,3 +166,8 @@ async function run (token) { help(); exit(1); } + +process.on('uncaughtException', (err) => { + handleError(err); + exit(1); +}); diff --git a/lib/secrets.js b/lib/secrets.js index b4b2820..3c3dc1e 100644 --- a/lib/secrets.js +++ b/lib/secrets.js @@ -2,7 +2,7 @@ import Now from '../lib'; export default class Secrets extends Now { - async ls () { + ls () { return this.retry(async (bail, attempt) => { if (this._debug) console.time(`> [debug] #${attempt} GET /secrets`); const res = await this._fetch('/secrets'); @@ -12,7 +12,7 @@ export default class Secrets extends Now { }); } - async rm (nameOrId) { + rm (nameOrId) { return this.retry(async (bail, attempt) => { if (this._debug) console.time(`> [debug] #${attempt} DELETE /secrets/${nameOrId}`); const res = await this._fetch(`/secrets/${nameOrId}`, { method: 'DELETE' }); @@ -22,15 +22,83 @@ export default class Secrets extends Now { return bail(new Error('Unauthorized')); } + const body = await res.json(); + + if (res.status !== 200) { + if (404 === res.status || 400 === res.status) { + const err = new Error(body.error.message); + err.userError = true; + return bail(err); + } else { + throw new Error(body.error.message); + } + } + + return body; + }); + } + + add (name, value) { + return this.retry(async (bail, attempt) => { + if (this._debug) console.time(`> [debug] #${attempt} POST /secrets`); + const res = await this._fetch('/secrets', { + method: 'POST', + body: { + name, + value + } + }); + if (this._debug) console.timeEnd(`> [debug] #${attempt} POST /secrets`); + + if (403 === res.status) { + return bail(new Error('Unauthorized')); + } + + const body = await res.json(); + if (res.status !== 200) { - const body = await res.json(); - throw new Error(body.error.message); + if (404 === res.status || 400 === res.status) { + const err = new Error(body.error.message); + err.userError = true; + return bail(err); + } else { + throw new Error(body.error.message); + } } + + return body; }); } - async add (name, value) { + rename (nameOrId, newName) { + return this.retry(async (bail, attempt) => { + if (this._debug) console.time(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`); + const res = await this._fetch('/secrets', { + method: 'PATCH', + body: JSON.stringify({ + name: newName + }) + }); + if (this._debug) console.timeEnd(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`); + + if (403 === res.status) { + return bail(new Error('Unauthorized')); + } + + const body = await res.json(); + if (res.status !== 200) { + if (404 === res.status || 400 === res.status) { + const err = new Error(body.error.message); + err.userError = true; + return bail(err); + } else { + throw new Error(body.error.message); + } + } + + return body; + }); } } diff --git a/package.json b/package.json index 2e0aaa4..6d999b5 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ ], "plugins": [ "transform-runtime", - "syntax-async-functions", "transform-async-to-generator" ] }, @@ -55,7 +54,7 @@ "arr-flatten": "1.0.1", "array-unique": "0.3.2", "async-retry": "0.2.1", - "babel-runtime": "6.11.6", + "babel-runtime": "6.5.0", "bytes": "2.4.0", "chalk": "1.1.3", "copy-paste": "1.3.0", @@ -84,7 +83,6 @@ "alpha-sort": "1.0.2", "ava": "^0.16.0", "babel-eslint": "6.1.2", - "babel-plugin-syntax-async-functions": "6.13.0", "babel-plugin-transform-async-to-generator": "6.8.0", "babel-plugin-transform-runtime": "6.12.0", "babel-preset-es2015": "6.13.2",