Browse Source

Make binaries comply to linting rules

master
Leo Lamprecht 8 years ago
parent
commit
e32bd818e4
No known key found for this signature in database GPG Key ID: EF804E3FF4BBA8AB
  1. 267
      bin/now-alias.js
  2. 271
      bin/now-certs.js
  3. 376
      bin/now-deploy.js
  4. 247
      bin/now-domains.js
  5. 137
      bin/now-list.js
  6. 156
      bin/now-remove.js
  7. 232
      bin/now-secrets.js
  8. 4
      package.json

267
bin/now-alias.js

@ -1,18 +1,18 @@
#!/usr/bin/env node #!/usr/bin/env node
// Packages // Packages
import chalk from 'chalk'; import chalk from 'chalk'
import minimist from 'minimist'; import minimist from 'minimist'
import table from 'text-table'; import table from 'text-table'
import ms from 'ms'; import ms from 'ms'
// Ours // Ours
import strlen from '../lib/strlen'; import strlen from '../lib/strlen'
import NowAlias from '../lib/alias'; import NowAlias from '../lib/alias'
import login from '../lib/login'; import login from '../lib/login'
import * as cfg from '../lib/cfg'; import * as cfg from '../lib/cfg'
import { error } from '../lib/error'; import {error} from '../lib/error'
import toHost from '../lib/to-host'; import toHost from '../lib/to-host'
const argv = minimist(process.argv.slice(2), { const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'], string: ['config', 'token'],
@ -23,8 +23,8 @@ const argv = minimist(process.argv.slice(2), {
debug: 'd', debug: 'd',
token: 't' token: 't'
} }
}); })
const subcommand = argv._[0]; const subcommand = argv._[0]
// options // options
const help = () => { const help = () => {
@ -70,209 +70,218 @@ const help = () => {
To get the list of alias ids, use ${chalk.dim('`now alias ls`')}. To get the list of alias ids, use ${chalk.dim('`now alias ls`')}.
${chalk.dim('Alias:')} ln ${chalk.dim('Alias:')} ln
`); `)
}; }
// options // options
const debug = argv.debug; const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'; const apiUrl = argv.url || 'https://api.zeit.co'
if (argv.config) cfg.setConfigFile(argv.config);
const exit = (code) => { if (argv.config) {
cfg.setConfigFile(argv.config)
}
const exit = code => {
// we give stdout some time to flush out // we give stdout some time to flush out
// because there's a node bug where // because there's a node bug where
// stdout writes are asynchronous // stdout writes are asynchronous
// https://github.com/nodejs/node/issues/6456 // https://github.com/nodejs/node/issues/6456
setTimeout(() => process.exit(code || 0), 100); setTimeout(() => process.exit(code || 0), 100)
}; }
if (argv.help || !subcommand) { if (argv.help || !subcommand) {
help(); help()
exit(0); exit(0)
} else { } else {
const config = cfg.read(); const config = cfg.read()
Promise.resolve(argv.token || config.token || login(apiUrl)) Promise.resolve(argv.token || config.token || login(apiUrl))
.then(async (token) => { .then(async token => {
try { try {
await run(token); await run(token)
} catch (err) { } catch (err) {
if (err.userError) { if (err.userError) {
error(err.message); error(err.message)
} else { } else {
error(`Unknown error: ${err.stack}`); error(`Unknown error: ${err.stack}`)
} }
exit(1); exit(1)
} }
}) })
.catch((e) => { .catch(e => {
error(`Authentication error – ${e.message}`); error(`Authentication error – ${e.message}`)
exit(1); exit(1)
}); })
} }
async function run(token) { async function run(token) {
const alias = new NowAlias(apiUrl, token, { debug }); const alias = new NowAlias(apiUrl, token, {debug})
const args = argv._.slice(1); const args = argv._.slice(1)
switch (subcommand) { switch (subcommand) {
case 'list': case 'list':
case 'ls': case 'ls':
if (0 !== args.length) { if (args.length !== 0) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias ls`')}`); error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias ls`')}`)
return exit(1); return exit(1)
} }
const start_ = new Date(); const start_ = new Date()
const list = await alias.list(); const list = await alias.list()
const urls = new Map(list.map(l => [l.uid, l.url])); const urls = new Map(list.map(l => [l.uid, l.url]))
const aliases = await alias.ls(); const aliases = await alias.ls()
aliases.sort((a, b) => new Date(b.created) - new Date(a.created)); aliases.sort((a, b) => new Date(b.created) - new Date(a.created))
const current = new Date(); const current = new Date()
const header = [['', 'id', 'source', 'url', 'created'].map(s => chalk.dim(s))]; const header = [['', 'id', 'source', 'url', 'created'].map(s => chalk.dim(s))]
const text = 0 === list.length ? null : table(header.concat(aliases.map((_alias) => { const text = list.length === 0 ? null : table(header.concat(aliases.map(_alias => {
const _url = chalk.underline(`https://${_alias.alias}`); const _url = chalk.underline(`https://${_alias.alias}`)
const target = _alias.deploymentId; const target = _alias.deploymentId
const _sourceUrl = urls.get(target) const _sourceUrl = urls.get(target) ? chalk.underline(`https://${urls.get(target)}`) : chalk.gray('<null>')
? chalk.underline(`https://${urls.get(target)}`)
: chalk.gray('<null>'); const time = chalk.gray(ms(current - new Date(_alias.created)) + ' ago')
const time = chalk.gray(ms(current - new Date(_alias.created)) + ' ago');
return [ return [
'', '',
// we default to `''` because some early aliases didn't // we default to `''` because some early aliases didn't
// have an uid associated // have an uid associated
null == _alias.uid ? '' : _alias.uid, _alias.uid === null ? '' : _alias.uid,
_sourceUrl, _sourceUrl,
_url, _url,
time time
]; ]
})), { align: ['l', 'r', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen }); })), {align: ['l', 'r', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen})
const elapsed_ = ms(new Date() - start_)
console.log(`> ${aliases.length} alias${aliases.length === 1 ? '' : 'es'} found ${chalk.gray(`[${elapsed_}]`)}`)
if (text) {
console.log('\n' + text + '\n')
}
const elapsed_ = ms(new Date() - start_); break
console.log(`> ${aliases.length} alias${aliases.length !== 1 ? 'es' : ''} found ${chalk.gray(`[${elapsed_}]`)}`);
if (text) console.log('\n' + text + '\n');
break;
case 'remove': case 'remove':
case 'rm': case 'rm':
const _target = String(args[0]); const _target = String(args[0])
if (!_target) { if (!_target) {
const err = new Error('No alias id specified'); const err = new Error('No alias id specified')
err.userError = true; err.userError = true
throw err; throw err
} }
if (1 !== args.length) { if (args.length !== 1) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias rm <id>`')}`); error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias rm <id>`')}`)
return exit(1); return exit(1)
} }
const _aliases = await alias.ls(); const _aliases = await alias.ls()
const _alias = findAlias(_target, _aliases); const _alias = findAlias(_target, _aliases)
if (!_alias) { if (!_alias) {
const err = new Error(`Alias not found by "${_target}". Run ${chalk.dim('`now alias ls`')} to see your aliases.`); const err = new Error(`Alias not found by "${_target}". Run ${chalk.dim('`now alias ls`')} to see your aliases.`)
err.userError = true; err.userError = true
throw err; throw err
} }
try { try {
const confirmation = (await readConfirmation(alias, _alias, _aliases)).toLowerCase(); const confirmation = (await readConfirmation(alias, _alias, _aliases)).toLowerCase()
if ('y' !== confirmation && 'yes' !== confirmation) { if (confirmation !== 'y' && confirmation !== 'yes') {
console.log('\n> Aborted'); console.log('\n> Aborted')
process.exit(0); process.exit(0)
} }
const start = new Date(); const start = new Date()
await alias.rm(_alias); await alias.rm(_alias)
const elapsed = ms(new Date() - start); const elapsed = ms(new Date() - start)
console.log(`${chalk.cyan('> Success!')} Alias ${chalk.bold(_alias.uid)} removed [${elapsed}]`); console.log(`${chalk.cyan('> Success!')} Alias ${chalk.bold(_alias.uid)} removed [${elapsed}]`)
} catch (err) { } catch (err) {
error(err); error(err)
exit(1); exit(1)
} }
break; break
case 'add': case 'add':
case 'set': case 'set':
if (2 !== args.length) { if (args.length !== 2) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias set <id> <domain>`')}`); error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias set <id> <domain>`')}`)
return exit(1); return exit(1)
} }
await alias.set(String(args[0]), String(args[1])); await alias.set(String(args[0]), String(args[1]))
break; break
default: default:
if (2 === argv._.length) { if (argv._.length === 2) {
await alias.set(String(argv._[0]), String(argv._[1])); await alias.set(String(argv._[0]), String(argv._[1]))
} else if (argv._.length >= 3) { } else if (argv._.length >= 3) {
error('Invalid number of arguments'); error('Invalid number of arguments')
help(); help()
exit(1); exit(1)
} else { } else {
error('Please specify a valid subcommand: ls | set | rm'); error('Please specify a valid subcommand: ls | set | rm')
help(); help()
exit(1); exit(1)
}
} }
alias.close();
} }
function indent (text, n) { alias.close()
return text.split('\n').map((l) => ' '.repeat(n) + l).join('\n');
} }
async function readConfirmation (alias, _alias, list) { async function readConfirmation(alias, _alias) {
const deploymentsList = await alias.list(); const deploymentsList = await alias.list()
const urls = new Map(deploymentsList.map(l => [l.uid, l.url])); const urls = new Map(deploymentsList.map(l => [l.uid, l.url]))
return new Promise((resolve, reject) => { return new Promise(resolve => {
const time = chalk.gray(ms(new Date() - new Date(_alias.created)) + ' ago'); const time = chalk.gray(ms(new Date() - new Date(_alias.created)) + ' ago')
const _sourceUrl = chalk.underline(`https://${urls.get(_alias.deploymentId)}`); const _sourceUrl = chalk.underline(`https://${urls.get(_alias.deploymentId)}`)
const tbl = table( const tbl = table(
[[_alias.uid, _sourceUrl, chalk.underline(`https://${_alias.alias}`), time]], [[_alias.uid, _sourceUrl, chalk.underline(`https://${_alias.alias}`), time]],
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} {align: ['l', 'r', 'l'], hsep: ' '.repeat(6)}
); )
process.stdout.write('> The following alias will be removed permanently\n'); process.stdout.write('> The following alias will be removed permanently\n')
process.stdout.write(' ' + tbl + '\n'); process.stdout.write(' ' + tbl + '\n')
process.stdout.write(` ${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`); process.stdout.write(` ${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`)
process.stdin.on('data', (d) => { process.stdin.on('data', d => {
process.stdin.pause(); process.stdin.pause()
resolve(d.toString().trim()); resolve(d.toString().trim())
}).resume(); }).resume()
}); })
} }
function findAlias(alias, list) { function findAlias(alias, list) {
let key, val; let key
let val
if (/\./.test(alias)) { if (/\./.test(alias)) {
val = toHost(alias); val = toHost(alias)
key = 'alias'; key = 'alias'
} else { } else {
val = alias; val = alias
key = 'uid'; key = 'uid'
} }
const _alias = list.find((d) => { const _alias = list.find(d => {
if (d[key] === val) { if (d[key] === val) {
if (debug) console.log(`> [debug] matched alias ${d.uid} by ${key} ${val}`); if (debug) {
return true; console.log(`> [debug] matched alias ${d.uid} by ${key} ${val}`)
}
return true
} }
// match prefix // match prefix
if (`${val}.now.sh` === d.alias) { if (`${val}.now.sh` === d.alias) {
if (debug) console.log(`> [debug] matched alias ${d.uid} by url ${d.host}`); if (debug) {
return true; console.log(`> [debug] matched alias ${d.uid} by url ${d.host}`)
} }
return false; return true
}); }
return false
})
return _alias; return _alias
} }

271
bin/now-certs.js

@ -1,20 +1,21 @@
#!/usr/bin/env node #!/usr/bin/env node
// Native // Native
import path from 'path'; import path from 'path'
// Packages // Packages
import chalk from 'chalk'; import chalk from 'chalk'
import table from 'text-table'; import table from 'text-table'
import minimist from 'minimist'; import minimist from 'minimist'
import fs from 'fs-promise'; import fs from 'fs-promise'
import ms from 'ms'; import ms from 'ms'
// Ours // Ours
import strlen from '../lib/strlen'; import strlen from '../lib/strlen'
import * as cfg from '../lib/cfg'; import * as cfg from '../lib/cfg'
import {handleError, error} from '../lib/error'; import {handleError, error} from '../lib/error'
import NowCerts from '../lib/certs'; import NowCerts from '../lib/certs'
import login from '../lib/login'
const argv = minimist(process.argv.slice(2), { const argv = minimist(process.argv.slice(2), {
string: ['config', 'token', 'crt', 'key', 'ca'], string: ['config', 'token', 'crt', 'key', 'ca'],
@ -25,8 +26,9 @@ const argv = minimist(process.argv.slice(2), {
debug: 'd', debug: 'd',
token: 't' token: 't'
} }
}); })
const subcommand = argv._[0];
const subcommand = argv._[0]
// options // options
const help = () => { const help = () => {
@ -64,190 +66,199 @@ const help = () => {
${chalk.gray('–')} Replacing an existing certificate with a user-supplied certificate: ${chalk.gray('–')} Replacing an existing certificate with a user-supplied certificate:
${chalk.cyan('$ now certs replace --crt domain.crt --key domain.key --ca ca_chain.crt domain.com')} ${chalk.cyan('$ now certs replace --crt domain.crt --key domain.key --ca ca_chain.crt domain.com')}
`); `)
}; }
// options // options
const debug = argv.debug; const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'; const apiUrl = argv.url || 'https://api.zeit.co'
if (argv.config) cfg.setConfigFile(argv.config);
if (argv.config) {
cfg.setConfigFile(argv.config)
}
const exit = (code) => { const exit = code => {
// we give stdout some time to flush out // we give stdout some time to flush out
// because there's a node bug where // because there's a node bug where
// stdout writes are asynchronous // stdout writes are asynchronous
// https://github.com/nodejs/node/issues/6456 // https://github.com/nodejs/node/issues/6456
setTimeout(() => process.exit(code || 0), 100); setTimeout(() => process.exit(code || 0), 100)
}; }
if (argv.help || !subcommand) { if (argv.help || !subcommand) {
help(); help()
exit(0); exit(0)
} else { } else {
const config = cfg.read(); const config = cfg.read()
Promise.resolve(argv.token || config.token || login(apiUrl)) Promise.resolve(argv.token || config.token || login(apiUrl))
.then(async (token) => { .then(async token => {
try { try {
await run(token); await run(token)
} catch (err) { } catch (err) {
handleError(err); handleError(err)
exit(1); exit(1)
} }
}) })
.catch((e) => { .catch(e => {
error(`Authentication error – ${e.message}`); error(`Authentication error – ${e.message}`)
exit(1); exit(1)
}); })
} }
function formatExpirationDate(date) { function formatExpirationDate(date) {
const diff = date - Date.now(); const diff = date - Date.now()
return diff < 0 ? chalk.gray(ms(new Date(-diff)) + ' ago') : chalk.gray('in ' + ms(new Date(diff))); return diff < 0 ? chalk.gray(ms(new Date(-diff)) + ' ago') : chalk.gray('in ' + ms(new Date(diff)))
} }
async function run(token) { async function run(token) {
const certs = new NowCerts(apiUrl, token, { debug }); const certs = new NowCerts(apiUrl, token, {debug})
const args = argv._.slice(1); const args = argv._.slice(1)
const start = Date.now(); const start = Date.now()
if ('ls' === subcommand || 'list' === subcommand) { if (subcommand === 'ls' || subcommand === 'list') {
if (0 !== args.length) { if (args.length !== 0) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs ls`')}`); error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs ls`')}`)
return exit(1); return exit(1)
} }
const list = await certs.ls();
const elapsed = ms(new Date() - start);
console.log(`> ${list.length} certificate${list.length !== 1 ? 's' : ''} found ${chalk.gray(`[${elapsed}]`)}`);
if (0 < list.length) { const list = await certs.ls()
const cur = Date.now(); const elapsed = ms(new Date() - start)
console.log(`> ${list.length} certificate${list.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`)
if (list.length > 0) {
const cur = Date.now()
list.sort((a, b) => { list.sort((a, b) => {
return a.cn.localeCompare(b.cn); return a.cn.localeCompare(b.cn)
}); })
const header = [['', 'id', 'cn', 'created', 'expiration'].map(s => chalk.dim(s))]; const header = [['', 'id', 'cn', 'created', 'expiration'].map(s => chalk.dim(s))]
const out = table(header.concat(list.map((cert) => { const out = table(header.concat(list.map(cert => {
const cn = chalk.bold(cert.cn); const cn = chalk.bold(cert.cn)
const time = chalk.gray(ms(cur - new Date(cert.created)) + ' ago'); const time = chalk.gray(ms(cur - new Date(cert.created)) + ' ago')
const expiration = formatExpirationDate(new Date(cert.expiration)); const expiration = formatExpirationDate(new Date(cert.expiration))
return [ return [
'', '',
cert.uid ? cert.uid : 'unknown', cert.uid ? cert.uid : 'unknown',
cn, cn,
time, time,
expiration expiration
]; ]
})), { align: ['l', 'r', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen }); })), {align: ['l', 'r', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen})
if (out) console.log('\n' + out + '\n');
} if (out) {
} else if ('create' === subcommand) { console.log('\n' + out + '\n')
if (1 !== args.length) { }
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs create <cn>`')}`); }
return exit(1); } else if (subcommand === 'create') {
} if (args.length !== 1) {
const cn = args[0]; error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs create <cn>`')}`)
const cert = await certs.create(cn); return exit(1)
const elapsed = ms(new Date() - start); }
console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cn)} ${chalk.gray(`(${cert.uid})`)} issued ${chalk.gray(`[${elapsed}]`)}`); const cn = args[0]
} else if ('renew' === subcommand) { const cert = await certs.create(cn)
if (1 !== args.length) { const elapsed = ms(new Date() - start)
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs renew <id | cn>`')}`); console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cn)} ${chalk.gray(`(${cert.uid})`)} issued ${chalk.gray(`[${elapsed}]`)}`)
return exit(1); } else if (subcommand === 'renew') {
} if (args.length !== 1) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs renew <id | cn>`')}`)
const cert = await getCertIdCn(certs, args[0]); return exit(1)
const yes = await readConfirmation(cert, 'The following certificate will be renewed\n'); }
const cert = await getCertIdCn(certs, args[0])
const yes = await readConfirmation(cert, 'The following certificate will be renewed\n')
if (!yes) { if (!yes) {
error('User abort'); error('User abort')
return exit(0); return exit(0)
} }
await certs.renew(cert.cn); await certs.renew(cert.cn)
const elapsed = ms(new Date() - start); const elapsed = ms(new Date() - start)
console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} renewed ${chalk.gray(`[${elapsed}]`)}`); console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} renewed ${chalk.gray(`[${elapsed}]`)}`)
} else if ('replace' === subcommand) { } else if (subcommand === 'replace') {
if (!argv.crt || !argv.key) { if (!argv.crt || !argv.key) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs replace --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`')}`); error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs replace --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`')}`)
return exit(1); return exit(1)
} }
const crt = readX509File(argv.crt); const crt = readX509File(argv.crt)
const key = readX509File(argv.key); const key = readX509File(argv.key)
const ca = argv.ca ? readX509File(argv.ca) : ''; const ca = argv.ca ? readX509File(argv.ca) : ''
const cert = await getCertIdCn(certs, args[0]); const cert = await getCertIdCn(certs, args[0])
const yes = await readConfirmation(cert, 'The following certificate will be replaced permanently\n'); const yes = await readConfirmation(cert, 'The following certificate will be replaced permanently\n')
if (!yes) { if (!yes) {
error('User abort'); error('User abort')
return exit(0); return exit(0)
} }
await certs.replace(cert.cn, crt, key, ca); await certs.replace(cert.cn, crt, key, ca)
const elapsed = ms(new Date() - start); const elapsed = ms(new Date() - start)
console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} replaced ${chalk.gray(`[${elapsed}]`)}`); console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} replaced ${chalk.gray(`[${elapsed}]`)}`)
} else if ('rm' === subcommand || 'remove' === subcommand) { } else if (subcommand === 'rm' || subcommand === 'remove') {
if (1 !== args.length) { if (args.length !== 1) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs rm <id | cn>`')}`); error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs rm <id | cn>`')}`)
return exit(1); return exit(1)
} }
const cert = await getCertIdCn(certs, args[0]); const cert = await getCertIdCn(certs, args[0])
const yes = await readConfirmation(cert, 'The following certificate will be removed permanently\n'); const yes = await readConfirmation(cert, 'The following certificate will be removed permanently\n')
if (!yes) { if (!yes) {
error('User abort'); error('User abort')
return exit(0); return exit(0)
} }
await certs.delete(cert.cn); await certs.delete(cert.cn)
const elapsed = ms(new Date() - start); const elapsed = ms(new Date() - start)
console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}`); console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}`)
} else { } else {
error('Please specify a valid subcommand: ls | create | renew | replace | rm'); error('Please specify a valid subcommand: ls | create | renew | replace | rm')
help(); help()
exit(1); exit(1)
} }
return certs.close(); return certs.close()
} }
process.on('uncaughtException', (err) => { process.on('uncaughtException', err => {
handleError(err); handleError(err)
exit(1); exit(1)
}); })
function readConfirmation(cert, msg) { function readConfirmation(cert, msg) {
return new Promise((resolve, reject) => { return new Promise(resolve => {
const time = chalk.gray(ms(new Date() - new Date(cert.created)) + ' ago'); const time = chalk.gray(ms(new Date() - new Date(cert.created)) + ' ago')
const tbl = table( const tbl = table(
[[cert.uid, chalk.bold(cert.cn), time]], [[cert.uid, chalk.bold(cert.cn), time]],
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} {align: ['l', 'r', 'l'], hsep: ' '.repeat(6)}
); )
process.stdout.write(`> ${msg}`); process.stdout.write(`> ${msg}`)
process.stdout.write(' ' + tbl + '\n'); process.stdout.write(' ' + tbl + '\n')
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`); process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`)
process.stdin.on('data', (d) => { process.stdin.on('data', d => {
process.stdin.pause(); process.stdin.pause()
resolve('y' === d.toString().trim().toLowerCase()); resolve(d.toString().trim().toLowerCase() === 'y')
}).resume(); }).resume()
}); })
} }
function readX509File(file) { function readX509File(file) {
return fs.readFileSync(path.resolve(file), 'utf8'); return fs.readFileSync(path.resolve(file), 'utf8')
} }
async function getCertIdCn(certs, idOrCn) { async function getCertIdCn(certs, idOrCn) {
const list = await certs.ls(); const list = await certs.ls()
const thecert = list.filter((cert) => { const thecert = list.filter(cert => {
return cert.uid === idOrCn || cert.cn === idOrCn; return cert.uid === idOrCn || cert.cn === idOrCn
})[0]; })[0]
if (!thecert) { if (!thecert) {
error(`No certificate found by id or cn "${idOrCn}"`); error(`No certificate found by id or cn "${idOrCn}"`)
return exit(1); return exit(1)
} }
return thecert; return thecert
} }

376
bin/now-deploy.js

@ -1,26 +1,26 @@
#!/usr/bin/env node #!/usr/bin/env node
// Native // Native
import {resolve} from 'path'; import {resolve} from 'path'
// Packages // Packages
import Progress from 'progress'; import Progress from 'progress'
import {stat} from 'fs-promise'; import {stat} from 'fs-promise'
import bytes from 'bytes'; import bytes from 'bytes'
import chalk from 'chalk'; import chalk from 'chalk'
import minimist from 'minimist'; import minimist from 'minimist'
import ms from 'ms'; import ms from 'ms'
// Ours // Ours
import copy from '../lib/copy'; import copy from '../lib/copy'
import login from '../lib/login'; import login from '../lib/login'
import * as cfg from '../lib/cfg'; import * as cfg from '../lib/cfg'
import {version} from '../../package'; import {version} from '../../package'
import Logger from '../lib/build-logger'; import Logger from '../lib/build-logger'
import Now from '../lib'; import Now from '../lib'
import toHumanPath from '../lib/utils/to-human-path'; import toHumanPath from '../lib/utils/to-human-path'
import promptOptions from '../lib/utils/prompt-options'; import promptOptions from '../lib/utils/prompt-options'
import {handleError, error} from '../lib/error'; import {handleError, error} from '../lib/error'
const argv = minimist(process.argv.slice(2), { const argv = minimist(process.argv.slice(2), {
string: [ string: [
@ -52,7 +52,7 @@ const argv = minimist(process.argv.slice(2), {
'no-clipboard': 'C', 'no-clipboard': 'C',
'forward-npm': 'N' 'forward-npm': 'N'
} }
}); })
const help = () => { const help = () => {
console.log(` console.log(`
@ -115,227 +115,243 @@ const help = () => {
${chalk.gray('–')} Displays comprehensive help for the subcommand ${chalk.dim('`list`')} ${chalk.gray('–')} Displays comprehensive help for the subcommand ${chalk.dim('`list`')}
${chalk.cyan('$ now help list')} ${chalk.cyan('$ now help list')}
`); `)
}; }
let path = argv._[0]; let path = argv._[0]
if (null != path) { if (path === null) {
path = process.cwd()
} else {
// if path is relative: resolve // if path is relative: resolve
// if path is absolute: clear up strange `/` etc // if path is absolute: clear up strange `/` etc
path = resolve(process.cwd(), path); path = resolve(process.cwd(), path)
} else {
path = process.cwd();
} }
const exit = (code) => { const exit = code => {
// we give stdout some time to flush out // we give stdout some time to flush out
// because there's a node bug where // because there's a node bug where
// stdout writes are asynchronous // stdout writes are asynchronous
// https://github.com/nodejs/node/issues/6456 // https://github.com/nodejs/node/issues/6456
setTimeout(() => process.exit(code || 0), 100); setTimeout(() => process.exit(code || 0), 100)
}; }
// options // options
const debug = argv.debug; const debug = argv.debug
const clipboard = !argv['no-clipboard']; const clipboard = !argv['no-clipboard']
const forwardNpm = argv['forward-npm']; const forwardNpm = argv['forward-npm']
const forceNew = argv.force; const forceNew = argv.force
const forceSync = argv.forceSync; const forceSync = argv.forceSync
const shouldLogin = argv.login; const shouldLogin = argv.login
const wantsPublic = argv.public; const wantsPublic = argv.public
const apiUrl = argv.url || 'https://api.zeit.co'; const apiUrl = argv.url || 'https://api.zeit.co'
const isTTY = process.stdout.isTTY; const isTTY = process.stdout.isTTY
const quiet = !isTTY; const quiet = !isTTY
if (argv.config) cfg.setConfigFile(argv.config);
const config = cfg.read(); if (argv.config) {
const alwaysForwardNpm = config.forwardNpm; cfg.setConfigFile(argv.config)
}
const config = cfg.read()
const alwaysForwardNpm = config.forwardNpm
if (argv.h || argv.help) { if (argv.h || argv.help) {
help(); help()
exit(0); exit(0)
} else if (argv.v || argv.version) { } else if (argv.v || argv.version) {
console.log(chalk.bold('𝚫 now'), version); console.log(chalk.bold('𝚫 now'), version)
process.exit(0); process.exit(0)
} else if (!(argv.token || config.token) || shouldLogin) { } else if (!(argv.token || config.token) || shouldLogin) {
login(apiUrl) login(apiUrl)
.then((token) => { .then(token => {
if (shouldLogin) { if (shouldLogin) {
console.log('> Logged in successfully. Token saved in ~/.now.json'); console.log('> Logged in successfully. Token saved in ~/.now.json')
process.exit(0); process.exit(0)
} else { } else {
sync(token).catch((err) => { sync(token).catch(err => {
error(`Unknown error: ${err.stack}`); error(`Unknown error: ${err.stack}`)
process.exit(1); process.exit(1)
}); })
} }
}) })
.catch((e) => { .catch(e => {
error(`Authentication error – ${e.message}`); error(`Authentication error – ${e.message}`)
process.exit(1); process.exit(1)
}); })
} else { } else {
sync(argv.token || config.token).catch((err) => { sync(argv.token || config.token).catch(err => {
error(`Unknown error: ${err.stack}`); error(`Unknown error: ${err.stack}`)
process.exit(1); process.exit(1)
}); })
} }
async function sync(token) { async function sync(token) {
const start = Date.now(); const start = Date.now()
if (!quiet) { if (!quiet) {
console.log(`> Deploying ${chalk.bold(toHumanPath(path))}`); console.log(`> Deploying ${chalk.bold(toHumanPath(path))}`)
} }
try { try {
await stat(path); await stat(path)
} catch (err) { } catch (err) {
error(`Could not read directory ${chalk.bold(path)}`); error(`Could not read directory ${chalk.bold(path)}`)
process.exit(1); process.exit(1)
} }
let deploymentType, hasPackage, hasDockerfile; let deploymentType
let hasPackage
let hasDockerfile
if (argv.docker) { if (argv.docker) {
if (debug) { if (debug) {
console.log(`> [debug] Forcing \`deploymentType\` = \`docker\``); console.log(`> [debug] Forcing \`deploymentType\` = \`docker\``)
} }
deploymentType = 'docker'; deploymentType = 'docker'
} else { } else if (argv.npm) {
if (argv.npm) { deploymentType = 'npm'
deploymentType = 'npm';
} else { } else {
try { try {
await stat(resolve(path, 'package.json')); await stat(resolve(path, 'package.json'))
} catch (err) { } catch (err) {
hasPackage = true; hasPackage = true
} }
[hasPackage, hasDockerfile] = await Promise.all([ [hasPackage, hasDockerfile] = await Promise.all([
await (async () => { await (async () => {
try { try {
await stat(resolve(path, 'package.json')); await stat(resolve(path, 'package.json'))
} catch (err) { } catch (err) {
return false; return false
} }
return true; return true
})(), })(),
await (async () => { await (async () => {
try { try {
await stat(resolve(path, 'Dockerfile')); await stat(resolve(path, 'Dockerfile'))
} catch (err) { } catch (err) {
return false; return false
} }
return true; return true
})() })()
]); ])
if (hasPackage && hasDockerfile) { if (hasPackage && hasDockerfile) {
if (debug) console.log('[debug] multiple manifests found, disambiguating'); if (debug) {
console.log('[debug] multiple manifests found, disambiguating')
}
if (isTTY) { if (isTTY) {
try { try {
console.log(`> Two manifests found. Press [${chalk.bold('n')}] to deploy or re-run with --flag`); console.log(`> Two manifests found. Press [${chalk.bold('n')}] to deploy or re-run with --flag`)
deploymentType = await promptOptions([ deploymentType = await promptOptions([
['npm', `${chalk.bold('package.json')}\t${chalk.gray(' --npm')} `], ['npm', `${chalk.bold('package.json')}\t${chalk.gray(' --npm')} `],
['docker', `${chalk.bold('Dockerfile')}\t${chalk.gray('--docker')} `] ['docker', `${chalk.bold('Dockerfile')}\t${chalk.gray('--docker')} `]
]); ])
} catch (err) { } catch (err) {
error(err.message); error(err.message)
process.exit(1); process.exit(1)
} }
} else { } else {
error('Ambiguous deployment (`package.json` and `Dockerfile` found). ' + error('Ambiguous deployment (`package.json` and `Dockerfile` found). ' +
'Please supply `--npm` or `--docker` to disambiguate.'); 'Please supply `--npm` or `--docker` to disambiguate.')
} }
} else if (hasPackage) { } else if (hasPackage) {
if (debug) console.log('[debug] `package.json` found, assuming `deploymentType` = `npm`'); if (debug) {
deploymentType = 'npm'; console.log('[debug] `package.json` found, assuming `deploymentType` = `npm`')
}
deploymentType = 'npm'
} else if (hasDockerfile) { } else if (hasDockerfile) {
if (debug) console.log('[debug] `Dockerfile` found, assuming `deploymentType` = `docker`'); if (debug) {
deploymentType = 'docker'; console.log('[debug] `Dockerfile` found, assuming `deploymentType` = `docker`')
} else {
error(`Could not access a ${chalk.dim('`package.json`')} or ${chalk.dim('`Dockerfile`')} in ${chalk.bold(path)}`);
console.log(`> To deploy statically, try: ${chalk.dim(chalk.underline('https://zeit.co/blog/serve-it-now'))}.`);
process.exit(1);
} }
deploymentType = 'docker'
} else {
error(`Could not access a ${chalk.dim('`package.json`')} or ${chalk.dim('`Dockerfile`')} in ${chalk.bold(path)}`)
console.log(`> To deploy statically, try: ${chalk.dim(chalk.underline('https://zeit.co/blog/serve-it-now'))}.`)
process.exit(1)
} }
} }
const now = new Now(apiUrl, token, { debug }); const now = new Now(apiUrl, token, {debug})
const envs = [].concat(argv.env || []); const envs = [].concat(argv.env || [])
let secrets; let secrets
const findSecret = async (uidOrName) => { const findSecret = async uidOrName => {
if (!secrets) secrets = await now.listSecrets(); if (!secrets) {
return secrets.filter((secret) => { secrets = await now.listSecrets()
return secret.name === uidOrName || secret.uid === uidOrName; }
});
}; return secrets.filter(secret => {
return secret.name === uidOrName || secret.uid === uidOrName
})
}
const env_ = await Promise.all(envs.map(async (kv) => { const env_ = await Promise.all(envs.map(async kv => {
const [key, ...rest] = kv.split('='); const [key, ...rest] = kv.split('=')
let val; let val
if (rest.length) { if (rest.length > 0) {
val = rest.join('='); val = rest.join('=')
} }
if (/[^A-z0-9_]/i.test(key)) { if (/[^A-z0-9_]/i.test(key)) {
error(`Invalid ${chalk.dim('-e')} key ${chalk.bold(`"${chalk.bold(key)}"`)}. Only letters, digits and underscores are allowed.`); error(`Invalid ${chalk.dim('-e')} key ${chalk.bold(`"${chalk.bold(key)}"`)}. Only letters, digits and underscores are allowed.`)
return process.exit(1); return process.exit(1)
} }
if ('' === key || null == key) { if (key === '' || key === null) {
error(`Invalid env option ${chalk.bold(`"${kv}"`)}`); error(`Invalid env option ${chalk.bold(`"${kv}"`)}`)
return process.exit(1); return process.exit(1)
} }
if (val == null) { if (val === null) {
if (!(key in process.env)) { if ((key in process.env)) {
error(`No value specified for env ${chalk.bold(`"${chalk.bold(key)}"`)} and it was not found in your env.`); console.log(`> Reading ${chalk.bold(`"${chalk.bold(key)}"`)} from your env (as no value was specified)`)
return process.exit(1);
} else {
console.log(`> Reading ${chalk.bold(`"${chalk.bold(key)}"`)} from your env (as no value was specified)`);
// escape value if it begins with @ // escape value if it begins with @
val = process.env[key].replace(/^\@/, '\\@'); val = process.env[key].replace(/^\@/, '\\@')
} else {
error(`No value specified for env ${chalk.bold(`"${chalk.bold(key)}"`)} and it was not found in your env.`)
return process.exit(1)
} }
} }
if ('@' === val[0]) { if (val[0] === '@') {
const uidOrName = val.substr(1); const uidOrName = val.substr(1)
const secrets = await findSecret(uidOrName); const secrets = await findSecret(uidOrName)
if (secrets.length === 0) { if (secrets.length === 0) {
if ('' === uidOrName) { if (uidOrName === '') {
error(`Empty reference provided for env key ${chalk.bold(`"${chalk.bold(key)}"`)}`); error(`Empty reference provided for env key ${chalk.bold(`"${chalk.bold(key)}"`)}`)
} else { } else {
error(`No secret found by uid or name ${chalk.bold(`"${uidOrName}"`)}`); error(`No secret found by uid or name ${chalk.bold(`"${uidOrName}"`)}`)
} }
return process.exit(1); return process.exit(1)
} else if (secrets.length > 1) { } else if (secrets.length > 1) {
error(`Ambiguous secret ${chalk.bold(`"${uidOrName}"`)} (matches ${chalk.bold(secrets.length)} secrets)`); error(`Ambiguous secret ${chalk.bold(`"${uidOrName}"`)} (matches ${chalk.bold(secrets.length)} secrets)`)
return process.exit(1); return process.exit(1)
} else {
val = { uid: secrets[0].uid };
} }
val = {uid: secrets[0].uid}
} }
return [ return [
key, key,
typeof val === 'string' typeof val === 'string' ? val.replace(/^\\@/, '@') : val
// add support for escaping the @ as \@ ]
? val.replace(/^\\@/, '@') }))
: val
]; const env = {}
}));
let env = {};
env_ env_
.filter(v => !!v) .filter(v => Boolean(v))
.forEach(([key, val]) => { .forEach(([key, val]) => {
if (key in env) console.log(`> ${chalk.yellow('NOTE:')} Overriding duplicate env key ${chalk.bold(`"${key}"`)}`); if (key in env) {
console.log(`> ${chalk.yellow('NOTE:')} Overriding duplicate env key ${chalk.bold(`"${key}"`)}`)
}
env[key] = val env[key] = val
}); })
try { try {
await now.create(path, { await now.create(path, {
@ -346,45 +362,49 @@ async function sync (token) {
forwardNpm: alwaysForwardNpm || forwardNpm, forwardNpm: alwaysForwardNpm || forwardNpm,
quiet, quiet,
wantsPublic wantsPublic
}); })
} catch (err) { } catch (err) {
if (debug) console.log(`> [debug] error: ${err.stack}`); if (debug) {
handleError(err); console.log(`> [debug] error: ${err.stack}`)
process.exit(1);
} }
const { url } = now; handleError(err)
const elapsed = ms(new Date() - start); process.exit(1)
}
const {url} = now
const elapsed = ms(new Date() - start)
if (isTTY) { if (isTTY) {
if (clipboard) { if (clipboard) {
try { try {
await copy(url); await copy(url)
console.log(`${chalk.cyan('> Ready!')} ${chalk.bold(url)} (copied to clipboard) [${elapsed}]`); console.log(`${chalk.cyan('> Ready!')} ${chalk.bold(url)} (copied to clipboard) [${elapsed}]`)
} catch (err) { } catch (err) {
console.log(`${chalk.cyan('> Ready!')} ${chalk.bold(url)} [${elapsed}]`); console.log(`${chalk.cyan('> Ready!')} ${chalk.bold(url)} [${elapsed}]`)
} }
} else { } else {
console.log(`> ${url} [${elapsed}]`); console.log(`> ${url} [${elapsed}]`)
} }
} else { } else {
process.stdout.write(url); process.stdout.write(url)
} }
const start_u = new Date(); const startU = new Date()
const complete = () => { const complete = () => {
if (!quiet) { if (!quiet) {
const elapsed_u = ms(new Date() - start_u); const elapsedU = ms(new Date() - startU)
console.log(`> Sync complete (${bytes(now.syncAmount)}) [${elapsed_u}] `); console.log(`> Sync complete (${bytes(now.syncAmount)}) [${elapsedU}] `)
console.log('> Initializing…'); console.log('> Initializing…')
} }
// close http2 agent // close http2 agent
now.close(); now.close()
// show build logs // show build logs
printLogs(now.host); printLogs(now.host)
}; }
if (now.syncAmount) { if (now.syncAmount) {
const bar = new Progress('> Upload [:bar] :percent :etas', { const bar = new Progress('> Upload [:bar] :percent :etas', {
@ -392,45 +412,45 @@ async function sync (token) {
complete: '=', complete: '=',
incomplete: '', incomplete: '',
total: now.syncAmount total: now.syncAmount
}); })
now.upload(); now.upload()
now.on('upload', ({names, data}) => { now.on('upload', ({names, data}) => {
const amount = data.length; const amount = data.length
if (debug) { if (debug) {
console.log(`> [debug] Uploaded: ${names.join(' ')} (${bytes(data.length)})`); console.log(`> [debug] Uploaded: ${names.join(' ')} (${bytes(data.length)})`)
} }
bar.tick(amount); bar.tick(amount)
}); })
now.on('complete', complete); now.on('complete', complete)
now.on('error', (err) => { now.on('error', err => {
error('Upload failed'); error('Upload failed')
handleError(err); handleError(err)
process.exit(1); process.exit(1)
}); })
} else { } else {
if (!quiet) { if (!quiet) {
console.log(`> Initializing…`); console.log(`> Initializing…`)
} }
// close http2 agent // close http2 agent
now.close(); now.close()
// show build logs // show build logs
printLogs(now.host); printLogs(now.host)
} }
} }
function printLogs(host) { function printLogs(host) {
// log build // log build
const logger = new Logger(host, { debug, quiet }); const logger = new Logger(host, {debug, quiet})
logger.on('close', () => { logger.on('close', () => {
if (!quiet) { if (!quiet) {
console.log(`${chalk.cyan('> Deployment complete!')}`); console.log(`${chalk.cyan('> Deployment complete!')}`)
} }
process.exit(0); process.exit(0)
}); })
} }

247
bin/now-domains.js

@ -1,18 +1,18 @@
#!/usr/bin/env node #!/usr/bin/env node
// Packages // Packages
import chalk from 'chalk'; import chalk from 'chalk'
import minimist from 'minimist'; import minimist from 'minimist'
import table from 'text-table'; import table from 'text-table'
import ms from 'ms'; import ms from 'ms'
// Ours // Ours
import login from '../lib/login'; import login from '../lib/login'
import * as cfg from '../lib/cfg'; import * as cfg from '../lib/cfg'
import { error } from '../lib/error'; import {error} from '../lib/error'
import toHost from '../lib/to-host'; import toHost from '../lib/to-host'
import strlen from '../lib/strlen'; import strlen from '../lib/strlen'
import NowDomains from '../lib/domains'; import NowDomains from '../lib/domains'
const argv = minimist(process.argv.slice(2), { const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'], string: ['config', 'token'],
@ -23,8 +23,8 @@ const argv = minimist(process.argv.slice(2), {
debug: 'd', debug: 'd',
token: 't' token: 't'
} }
}); })
const subcommand = argv._[0]; const subcommand = argv._[0]
// options // options
const help = () => { const help = () => {
@ -75,192 +75,201 @@ const help = () => {
${chalk.cyan('$ now domain rm domainId')} ${chalk.cyan('$ now domain rm domainId')}
To get the list of domain ids, use ${chalk.dim('`now domains ls`')}. To get the list of domain ids, use ${chalk.dim('`now domains ls`')}.
`); `)
}; }
// options // options
const debug = argv.debug; const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'; const apiUrl = argv.url || 'https://api.zeit.co'
if (argv.config) cfg.setConfigFile(argv.config);
if (argv.config) {
cfg.setConfigFile(argv.config)
}
const exit = (code) => { const exit = code => {
// we give stdout some time to flush out // we give stdout some time to flush out
// because there's a node bug where // because there's a node bug where
// stdout writes are asynchronous // stdout writes are asynchronous
// https://github.com/nodejs/node/issues/6456 // https://github.com/nodejs/node/issues/6456
setTimeout(() => process.exit(code || 0), 100); setTimeout(() => process.exit(code || 0), 100)
}; }
if (argv.help || !subcommand) { if (argv.help || !subcommand) {
help(); help()
exit(0); exit(0)
} else { } else {
const config = cfg.read(); const config = cfg.read()
Promise.resolve(argv.token || config.token || login(apiUrl)) Promise.resolve(argv.token || config.token || login(apiUrl))
.then(async (token) => { .then(async token => {
try { try {
await run(token); await run(token)
} catch (err) { } catch (err) {
if (err.userError) { if (err.userError) {
error(err.message); error(err.message)
} else { } else {
error(`Unknown error: ${err.stack}`); error(`Unknown error: ${err.stack}`)
} }
exit(1); exit(1)
} }
}) })
.catch((e) => { .catch(e => {
error(`Authentication error – ${e.message}`); error(`Authentication error – ${e.message}`)
exit(1); exit(1)
}); })
} }
async function run(token) { async function run(token) {
const domain = new NowDomains(apiUrl, token, { debug }); const domain = new NowDomains(apiUrl, token, {debug})
const args = argv._.slice(1); const args = argv._.slice(1)
switch (subcommand) { switch (subcommand) {
case 'ls': case 'ls':
case 'list': case 'list':
if (0 !== args.length) { if (args.length !== 0) {
error('Invalid number of arguments'); error('Invalid number of arguments')
return exit(1); return exit(1)
} }
const start_ = new Date(); const start_ = new Date()
const domains = await domain.ls(); const domains = await domain.ls()
domains.sort((a, b) => new Date(b.created) - new Date(a.created)); domains.sort((a, b) => new Date(b.created) - new Date(a.created))
const current = new Date(); const current = new Date()
const header = [['', 'id', 'dns', 'url', 'created'].map(s => chalk.dim(s))]; const header = [['', 'id', 'dns', 'url', 'created'].map(s => chalk.dim(s))]
const out = domains.length === 0 ? null : table(header.concat(domains.map((domain) => { const out = domains.length === 0 ? null : table(header.concat(domains.map(domain => {
const ns = domain.isExternal ? 'external' : 'zeit.world'; const ns = domain.isExternal ? 'external' : 'zeit.world'
const url = chalk.underline(`https://${domain.name}`); const url = chalk.underline(`https://${domain.name}`)
const time = chalk.gray(ms(current - new Date(domain.created)) + ' ago'); const time = chalk.gray(ms(current - new Date(domain.created)) + ' ago')
return [ return [
'', '',
domain.uid, domain.uid,
ns, ns,
url, url,
time time
]; ]
})), { align: ['l', 'r', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen }); })), {align: ['l', 'r', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen})
const elapsed_ = ms(new Date() - start_); const elapsed_ = ms(new Date() - start_)
console.log(`> ${domains.length} domain${domains.length !== 1 ? 's' : ''} found ${chalk.gray(`[${elapsed_}]`)}`); console.log(`> ${domains.length} domain${domains.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed_}]`)}`)
if (out) console.log('\n' + out + '\n');
break; if (out) {
console.log('\n' + out + '\n')
}
break
case 'rm': case 'rm':
case 'remove': case 'remove':
if (1 !== args.length) { if (args.length !== 1) {
error('Invalid number of arguments'); error('Invalid number of arguments')
return exit(1); return exit(1)
} }
const _target = String(args[0]); const _target = String(args[0])
if (!_target) { if (!_target) {
const err = new Error('No domain specified'); const err = new Error('No domain specified')
err.userError = true; err.userError = true
throw err; throw err
} }
const _domains = await domain.ls(); const _domains = await domain.ls()
const _domain = findDomain(_target, _domains); const _domain = findDomain(_target, _domains)
if (!_domain) { if (!_domain) {
const err = new Error(`Domain not found by "${_target}". Run ${chalk.dim('`now domains ls`')} to see your domains.`); const err = new Error(`Domain not found by "${_target}". Run ${chalk.dim('`now domains ls`')} to see your domains.`)
err.userError = true; err.userError = true
throw err; throw err
} }
try { try {
const confirmation = (await readConfirmation(domain, _domain, _domains)).toLowerCase(); const confirmation = (await readConfirmation(domain, _domain, _domains)).toLowerCase()
if ('y' !== confirmation && 'yes' !== confirmation) { if (confirmation !== 'y' && confirmation !== 'yes') {
console.log('\n> Aborted'); console.log('\n> Aborted')
process.exit(0); process.exit(0)
} }
const start = new Date(); const start = new Date()
await domain.rm(_domain.name); await domain.rm(_domain.name)
const elapsed = ms(new Date() - start); const elapsed = ms(new Date() - start)
console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(_domain.uid)} removed [${elapsed}]`); console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(_domain.uid)} removed [${elapsed}]`)
} catch (err) { } catch (err) {
error(err); error(err)
exit(1); exit(1)
} }
break; break
case 'add': case 'add':
case 'set': case 'set':
if (1 !== args.length) { if (args.length !== 1) {
error('Invalid number of arguments'); error('Invalid number of arguments')
return exit(1); return exit(1)
} }
const start = new Date(); const start = new Date()
const name = String(args[0]); const name = String(args[0])
const { uid, created } = await domain.add(name); const {uid, created} = await domain.add(name)
const elapsed = ms(new Date() - start); const elapsed = ms(new Date() - start)
if (created) { if (created) {
console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} added [${elapsed}]`); console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} added [${elapsed}]`)
} else { } else {
console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} already exists [${elapsed}]`); console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} already exists [${elapsed}]`)
} }
break; break
default: default:
error('Please specify a valid subcommand: ls | add | rm'); error('Please specify a valid subcommand: ls | add | rm')
help(); help()
exit(1); exit(1)
}
domain.close();
} }
function indent (text, n) { domain.close()
return text.split('\n').map((l) => ' '.repeat(n) + l).join('\n');
} }
async function readConfirmation (domain, _domain, list) { async function readConfirmation(domain, _domain) {
const urls = new Map(list.map(l => [l.uid, l.url])); return new Promise(resolve => {
const time = chalk.gray(ms(new Date() - new Date(_domain.created)) + ' ago')
return new Promise((resolve, reject) => {
const time = chalk.gray(ms(new Date() - new Date(_domain.created)) + ' ago');
const tbl = table( const tbl = table(
[[_domain.uid, chalk.underline(`https://${_domain.name}`), time]], [[_domain.uid, chalk.underline(`https://${_domain.name}`), time]],
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} {align: ['l', 'r', 'l'], hsep: ' '.repeat(6)}
); )
process.stdout.write('> The following domain will be removed permanently\n'); process.stdout.write('> The following domain will be removed permanently\n')
process.stdout.write(' ' + tbl + '\n'); process.stdout.write(' ' + tbl + '\n')
if (_domain.aliases.length) {
process.stdout.write(`> ${chalk.yellow('Warning!')} This domain's ` if (_domain.aliases.length > 0) {
+ `${chalk.bold(_domain.aliases.length + ' alias' + (_domain.aliases.length !== 1 ? 'es': ''))} ` process.stdout.write(`> ${chalk.yellow('Warning!')} This domain's ` +
+ `will be removed. Run ${chalk.dim('`now alias ls`')} to list.\n`); `${chalk.bold(_domain.aliases.length + ' alias' + (_domain.aliases.length === 1 ? '' : 'es'))} ` +
`will be removed. Run ${chalk.dim('`now alias ls`')} to list.\n`)
} }
process.stdout.write(` ${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`);
process.stdin.on('data', (d) => { process.stdout.write(` ${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`)
process.stdin.pause();
resolve(d.toString().trim()); process.stdin.on('data', d => {
}).resume(); process.stdin.pause()
}); resolve(d.toString().trim())
}).resume()
})
} }
function findDomain(val, list) { function findDomain(val, list) {
return list.find((d) => { return list.find(d => {
if (d.uid === val) { if (d.uid === val) {
if (debug) console.log(`> [debug] matched domain ${d.uid} by uid`); if (debug) {
return true; console.log(`> [debug] matched domain ${d.uid} by uid`)
}
return true
} }
// match prefix // match prefix
if (d.name === toHost(val)) { if (d.name === toHost(val)) {
if (debug) console.log(`> [debug] matched domain ${d.uid} by name ${d.name}`); if (debug) {
return true; console.log(`> [debug] matched domain ${d.uid} by name ${d.name}`)
} }
return false; return true
}); }
return false
})
} }

137
bin/now-list.js

@ -1,18 +1,18 @@
#!/usr/bin/env node #!/usr/bin/env node
// Packages // Packages
import fs from 'fs-promise'; import fs from 'fs-promise'
import minimist from 'minimist'; import minimist from 'minimist'
import chalk from 'chalk'; import chalk from 'chalk'
import table from 'text-table'; import table from 'text-table'
import ms from 'ms'; import ms from 'ms'
// Ours // Ours
import strlen from '../lib/strlen'; import strlen from '../lib/strlen'
import Now from '../lib'; import Now from '../lib'
import login from '../lib/login'; import login from '../lib/login'
import * as cfg from '../lib/cfg'; import * as cfg from '../lib/cfg'
import {handleError, error} from '../lib/error'; import {handleError, error} from '../lib/error'
const argv = minimist(process.argv.slice(2), { const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'], string: ['config', 'token'],
@ -23,7 +23,7 @@ const argv = minimist(process.argv.slice(2), {
debug: 'd', debug: 'd',
token: 't' token: 't'
} }
}); })
const help = () => { const help = () => {
console.log(` console.log(`
@ -47,95 +47,110 @@ const help = () => {
${chalk.cyan('$ now ls my-app')} ${chalk.cyan('$ now ls my-app')}
${chalk.dim('Alias:')} ls ${chalk.dim('Alias:')} ls
`); `)
}; }
if (argv.help) { if (argv.help) {
help(); help()
process.exit(0); process.exit(0)
} }
const app = argv._[0]; const app = argv._[0]
// options // options
const debug = argv.debug; const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'; const apiUrl = argv.url || 'https://api.zeit.co'
if (argv.config) cfg.setConfigFile(argv.config);
const config = cfg.read(); if (argv.config) {
cfg.setConfigFile(argv.config)
}
const config = cfg.read()
Promise.resolve(argv.token || config.token || login(apiUrl)) Promise.resolve(argv.token || config.token || login(apiUrl))
.then(async (token) => { .then(async token => {
try { try {
await list(token); await list(token)
} catch (err) { } catch (err) {
error(`Unknown error: ${err.stack}`); error(`Unknown error: ${err.stack}`)
process.exit(1); process.exit(1)
} }
}) })
.catch((e) => { .catch(e => {
error(`Authentication error – ${e.message}`); error(`Authentication error – ${e.message}`)
process.exit(1); process.exit(1)
}); })
async function list(token) { async function list(token) {
const now = new Now(apiUrl, token, { debug }); const now = new Now(apiUrl, token, {debug})
const start = new Date(); const start = new Date()
let deployments; let deployments
try { try {
deployments = await now.list(app); deployments = await now.list(app)
} catch (err) { } catch (err) {
handleError(err); handleError(err)
process.exit(1); process.exit(1)
} }
now.close(); now.close()
const apps = new Map()
const apps = new Map();
for (const dep of deployments) { for (const dep of deployments) {
const deps = apps.get(dep.name) || []; const deps = apps.get(dep.name) || []
apps.set(dep.name, deps.concat(dep)); apps.set(dep.name, deps.concat(dep))
} }
const sorted = await sort([...apps]); const sorted = await sort([...apps])
const current = Date.now()
const current = Date.now();
const text = sorted.map(([name, deps]) => { const text = sorted.map(([name, deps]) => {
const t = table(deps.map(({uid, url, created}) => { const t = table(deps.map(({uid, url, created}) => {
const _url = chalk.underline(`https://${url}`); const _url = chalk.underline(`https://${url}`)
const time = chalk.gray(ms(current - created) + ' ago'); const time = chalk.gray(ms(current - created) + ' ago')
return [uid, _url, time]; return [uid, _url, time]
}), { align: ['l', 'r', 'l'], hsep: ' '.repeat(6), stringLength: strlen }); }), {align: ['l', 'r', 'l'], hsep: ' '.repeat(6), stringLength: strlen})
return chalk.bold(name) + '\n\n' + indent(t, 2); return chalk.bold(name) + '\n\n' + indent(t, 2)
}).join('\n\n'); }).join('\n\n')
const elapsed = ms(new Date() - start);
console.log(`> ${deployments.length} deployment${deployments.length !== 1 ? 's' : ''} found ${chalk.gray(`[${elapsed}]`)}`); const elapsed = ms(new Date() - start)
if (text) console.log('\n' + text + '\n'); console.log(`> ${deployments.length} deployment${deployments.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`)
if (text) {
console.log('\n' + text + '\n')
}
} }
async function sort(apps) { async function sort(apps) {
let pkg; let pkg
try { try {
const json = await fs.readFile('package.json'); const json = await fs.readFile('package.json')
pkg = JSON.parse(json); pkg = JSON.parse(json)
} catch (err) { } catch (err) {
pkg = {}; pkg = {}
} }
return apps return apps
.map(([name, deps]) => { .map(([name, deps]) => {
deps = deps.slice().sort((a, b) => { deps = deps.slice().sort((a, b) => {
return b.created - a.created; return b.created - a.created
}); })
return [name, deps]; return [name, deps]
}) })
.sort(([nameA, depsA], [nameB, depsB]) => { .sort(([nameA, depsA], [nameB, depsB]) => {
if (pkg.name === nameA) return -1; if (pkg.name === nameA) {
if (pkg.name === nameB) return 1; return -1
return depsB[0].created - depsA[0].created; }
});
if (pkg.name === nameB) {
return 1
}
return depsB[0].created - depsA[0].created
})
} }
function indent(text, n) { function indent(text, n) {
return text.split('\n').map((l) => ' '.repeat(n) + l).join('\n'); return text.split('\n').map(l => ' '.repeat(n) + l).join('\n')
} }

156
bin/now-remove.js

@ -1,16 +1,16 @@
#!/usr/bin/env node #!/usr/bin/env node
// Packages // Packages
import minimist from 'minimist'; import minimist from 'minimist'
import chalk from 'chalk'; import chalk from 'chalk'
import ms from 'ms'; import ms from 'ms'
import table from 'text-table'; import table from 'text-table'
// Ours // Ours
import Now from '../lib'; import Now from '../lib'
import login from '../lib/login'; import login from '../lib/login'
import * as cfg from '../lib/cfg'; import * as cfg from '../lib/cfg'
import {handleError, error} from '../lib/error'; import {handleError, error} from '../lib/error'
const argv = minimist(process.argv.slice(2), { const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'], string: ['config', 'token'],
@ -21,9 +21,9 @@ const argv = minimist(process.argv.slice(2), {
debug: 'd', debug: 'd',
token: 't' token: 't'
} }
}); })
const ids = argv._; const ids = argv._
// options // options
const help = () => { const help = () => {
@ -52,118 +52,120 @@ const help = () => {
${chalk.cyan('$ now rm eyWt6zuSdeus uWHoA9RQ1d1o')} ${chalk.cyan('$ now rm eyWt6zuSdeus uWHoA9RQ1d1o')}
${chalk.dim('Alias:')} rm ${chalk.dim('Alias:')} rm
`); `)
}; }
if (argv.help || 0 === ids.length) { if (argv.help || ids.length === 0) {
help(); help()
process.exit(0); process.exit(0)
} }
// options // options
const debug = argv.debug; const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'; const apiUrl = argv.url || 'https://api.zeit.co'
const hard = argv.hard || false; const hard = argv.hard || false
if (argv.config) cfg.setConfigFile(argv.config);
const config = cfg.read();
function readConfirmation (matches) { if (argv.config) {
return new Promise((resolve, reject) => { cfg.setConfigFile(argv.config)
}
const config = cfg.read()
process.stdout.write(`> The following deployment${matches.length === 1 ? '' : 's'} will be removed permanently:\n`); function readConfirmation(matches) {
return new Promise(resolve => {
process.stdout.write(`> The following deployment${matches.length === 1 ? '' : 's'} will be removed permanently:\n`)
const tbl = table( const tbl = table(
matches.map((depl) => { matches.map(depl => {
const time = chalk.gray(ms(new Date() - depl.created) + ' ago'); const time = chalk.gray(ms(new Date() - depl.created) + ' ago')
const url = chalk.underline(`https://${depl.url}`); const url = chalk.underline(`https://${depl.url}`)
return [depl.uid, url, time]; return [depl.uid, url, time]
}), }),
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} {align: ['l', 'r', 'l'], hsep: ' '.repeat(6)}
); )
process.stdout.write(tbl + '\n'); process.stdout.write(tbl + '\n')
for (let depl of matches) { for (const depl of matches) {
for (let alias of depl.aliases) { for (const alias of depl.aliases) {
process.stdout.write( process.stdout.write(
`> ${chalk.yellow('Warning!')} Deployment ${chalk.bold(depl.uid)} ` + `> ${chalk.yellow('Warning!')} Deployment ${chalk.bold(depl.uid)} ` +
`is an alias for ${chalk.underline(`https://${alias.alias}`)} and will be removed.\n` `is an alias for ${chalk.underline(`https://${alias.alias}`)} and will be removed.\n`
); )
} }
} }
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`); process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`)
process.stdin.on('data', (d) => { process.stdin.on('data', d => {
process.stdin.pause(); process.stdin.pause()
resolve(d.toString().trim()); resolve(d.toString().trim())
}).resume(); }).resume()
}); })
} }
Promise.resolve(argv.token || config.token || login(apiUrl)) Promise.resolve(argv.token || config.token || login(apiUrl))
.then(async (token) => { .then(async token => {
try { try {
await remove(token); await remove(token)
} catch (err) { } catch (err) {
error(`Unknown error: ${err.stack}`); error(`Unknown error: ${err.stack}`)
process.exit(1); process.exit(1)
} }
}) })
.catch((e) => { .catch(e => {
error(`Authentication error – ${e.message}`); error(`Authentication error – ${e.message}`)
process.exit(1); process.exit(1)
}); })
async function remove(token) { async function remove(token) {
const now = new Now(apiUrl, token, { debug }); const now = new Now(apiUrl, token, {debug})
const deployments = await now.list(); const deployments = await now.list()
const matches = deployments.filter((d) => { const matches = deployments.filter(d => {
return ids.find((id) => { return ids.find(id => {
// `url` should match the hostname of the deployment // `url` should match the hostname of the deployment
let u = id.replace(/^https\:\/\//i, ''); let u = id.replace(/^https\:\/\//i, '')
if (-1 === u.indexOf('.')) {
if (u.indexOf('.') === -1) {
// `.now.sh` domain is implied if just the subdomain is given // `.now.sh` domain is implied if just the subdomain is given
u += '.now.sh'; u += '.now.sh'
} }
return d.uid === id return d.uid === id || d.name === id || d.url === u
|| d.name === id })
|| d.url === u; })
});
});
if (0 === matches.length) { if (matches.length === 0) {
error(`Could not find any deployments matching ${ids.map((id) => chalk.bold(`"${id}"`)).join(', ')}. Run ${chalk.dim(`\`now ls\``)} to list.`); error(`Could not find any deployments matching ${ids.map(id => chalk.bold(`"${id}"`)).join(', ')}. Run ${chalk.dim(`\`now ls\``)} to list.`)
return process.exit(1); return process.exit(1)
} }
const aliases = await Promise.all(matches.map((depl) => now.listAliases(depl.uid))); const aliases = await Promise.all(matches.map(depl => now.listAliases(depl.uid)))
for (let i = 0; i < matches.length; i++) { for (let i = 0; i < matches.length; i++) {
matches[i].aliases = aliases[i]; matches[i].aliases = aliases[i]
} }
try { try {
const confirmation = (await readConfirmation(matches)).toLowerCase(); const confirmation = (await readConfirmation(matches)).toLowerCase()
if ('y' !== confirmation && 'yes' !== confirmation) { if (confirmation !== 'y' && confirmation !== 'yes') {
console.log('\n> Aborted'); console.log('\n> Aborted')
process.exit(0); process.exit(0)
} }
const start = new Date(); const start = new Date()
await Promise.all(matches.map((depl) => now.remove(depl.uid, { hard }))); await Promise.all(matches.map(depl => now.remove(depl.uid, {hard})))
const elapsed = ms(new Date() - start); const elapsed = ms(new Date() - start)
console.log(`${chalk.cyan('> Success!')} [${elapsed}]`); console.log(`${chalk.cyan('> Success!')} [${elapsed}]`)
console.log(table(matches.map((depl) => { console.log(table(matches.map(depl => {
return [ `Deployment ${chalk.bold(depl.uid)} removed` ]; return [`Deployment ${chalk.bold(depl.uid)} removed`]
}))); })))
} catch (err) { } catch (err) {
handleError(err); handleError(err)
process.exit(1); process.exit(1)
} }
now.close(); now.close()
} }

232
bin/now-secrets.js

@ -1,17 +1,17 @@
#!/usr/bin/env node #!/usr/bin/env node
// Packages // Packages
import chalk from 'chalk'; import chalk from 'chalk'
import table from 'text-table'; import table from 'text-table'
import minimist from 'minimist'; import minimist from 'minimist'
import ms from 'ms'; import ms from 'ms'
// Ours // Ours
import strlen from '../lib/strlen'; import strlen from '../lib/strlen'
import * as cfg from '../lib/cfg'; import * as cfg from '../lib/cfg'
import {handleError, error} from '../lib/error'; import {handleError, error} from '../lib/error'
import NowSecrets from '../lib/secrets'; import NowSecrets from '../lib/secrets'
import login from '../lib/login'; import login from '../lib/login'
const argv = minimist(process.argv.slice(2), { const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'], string: ['config', 'token'],
@ -23,8 +23,9 @@ const argv = minimist(process.argv.slice(2), {
base64: 'b', base64: 'b',
token: 't' token: 't'
} }
}); })
const subcommand = argv._[0];
const subcommand = argv._[0]
// options // options
const help = () => { const help = () => {
@ -66,160 +67,173 @@ const help = () => {
${chalk.gray('–')} Removes a secret: ${chalk.gray('–')} Removes a secret:
${chalk.cyan(`$ now secrets rm my-secret`)} ${chalk.cyan(`$ now secrets rm my-secret`)}
`); `)
}; }
// options // options
const debug = argv.debug; const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'; const apiUrl = argv.url || 'https://api.zeit.co'
if (argv.config) cfg.setConfigFile(argv.config);
const exit = (code) => { if (argv.config) {
cfg.setConfigFile(argv.config)
}
const exit = code => {
// we give stdout some time to flush out // we give stdout some time to flush out
// because there's a node bug where // because there's a node bug where
// stdout writes are asynchronous // stdout writes are asynchronous
// https://github.com/nodejs/node/issues/6456 // https://github.com/nodejs/node/issues/6456
setTimeout(() => process.exit(code || 0), 100); setTimeout(() => process.exit(code || 0), 100)
}; }
if (argv.help || !subcommand) { if (argv.help || !subcommand) {
help(); help()
exit(0); exit(0)
} else { } else {
const config = cfg.read(); const config = cfg.read()
Promise.resolve(argv.token || config.token || login(apiUrl)) Promise.resolve(argv.token || config.token || login(apiUrl))
.then(async (token) => { .then(async token => {
try { try {
await run(token); await run(token)
} catch (err) { } catch (err) {
handleError(err); handleError(err)
exit(1); exit(1)
} }
}) })
.catch((e) => { .catch(e => {
error(`Authentication error – ${e.message}`); error(`Authentication error – ${e.message}`)
exit(1); exit(1)
}); })
} }
async function run(token) { async function run(token) {
const secrets = new NowSecrets(apiUrl, token, { debug }); const secrets = new NowSecrets(apiUrl, token, {debug})
const args = argv._.slice(1); const args = argv._.slice(1)
const start = Date.now(); const start = Date.now()
if ('ls' === subcommand || 'list' === subcommand) { if (subcommand === 'ls' || subcommand === 'list') {
if (0 !== args.length) { if (args.length !== 0) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret ls`')}`); error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret ls`')}`)
return exit(1); return exit(1)
} }
const list = await secrets.ls();
const elapsed = ms(new Date() - start); const list = await secrets.ls()
console.log(`> ${list.length} secret${list.length !== 1 ? 's' : ''} found ${chalk.gray(`[${elapsed}]`)}`); const elapsed = ms(new Date() - start)
if (0 < list.length) {
const cur = Date.now(); console.log(`> ${list.length} secret${list.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`)
const header = [['', 'id', 'name', 'created'].map(s => chalk.dim(s))];
const out = table(header.concat(list.map((secret) => { if (list.length > 0) {
const cur = Date.now()
const header = [['', 'id', 'name', 'created'].map(s => chalk.dim(s))]
const out = table(header.concat(list.map(secret => {
return [ return [
'', '',
secret.uid, secret.uid,
chalk.bold(secret.name), chalk.bold(secret.name),
chalk.gray(ms(cur - new Date(secret.created)) + ' ago') chalk.gray(ms(cur - new Date(secret.created)) + ' ago')
]; ]
})), { align: ['l', 'r', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen }); })), {align: ['l', 'r', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen})
if (out) console.log('\n' + out + '\n');
if (out) {
console.log('\n' + out + '\n')
}
} }
return secrets.close(); return secrets.close()
} }
if ('rm' === subcommand || 'remove' === subcommand) { if (subcommand === 'rm' || subcommand === 'remove') {
if (1 !== args.length) { if (args.length !== 1) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rm <id | name>`')}`); error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rm <id | name>`')}`)
return exit(1); return exit(1)
} }
const list = await secrets.ls(); const list = await secrets.ls()
const theSecret = list.filter((secret) => { const theSecret = list.filter(secret => {
return secret.uid === args[0] return secret.uid === args[0] || secret.name === args[0]
|| secret.name === args[0]; })[0]
})[0];
if (theSecret) { if (theSecret) {
const yes = await readConfirmation(theSecret); const yes = await readConfirmation(theSecret)
if (!yes) { if (!yes) {
error('User abort'); error('User abort')
return exit(0); return exit(0)
} }
} else { } else {
error(`No secret found by id or name "${args[0]}"`); error(`No secret found by id or name "${args[0]}"`)
return exit(1); return exit(1)
} }
const secret = await secrets.rm(args[0]); const secret = await secrets.rm(args[0])
const elapsed = ms(new Date() - start); const elapsed = ms(new Date() - start)
console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(secret.name)} ${chalk.gray(`(${secret.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}`); console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(secret.name)} ${chalk.gray(`(${secret.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}`)
return secrets.close(); return secrets.close()
} }
if ('rename' === subcommand) { if (subcommand === 'rename') {
if (2 !== args.length) { if (args.length !== 2) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rename <old-name> <new-name>`')}`); error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rename <old-name> <new-name>`')}`)
return exit(1); return exit(1)
} }
const secret = await secrets.rename(args[0], args[1]); const secret = await secrets.rename(args[0], args[1])
const elapsed = ms(new Date() - start); const elapsed = ms(new Date() - start)
console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(secret.oldName)} ${chalk.gray(`(${secret.uid})`)} renamed to ${chalk.bold(args[1])} ${chalk.gray(`[${elapsed}]`)}`); console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(secret.oldName)} ${chalk.gray(`(${secret.uid})`)} renamed to ${chalk.bold(args[1])} ${chalk.gray(`[${elapsed}]`)}`)
return secrets.close(); return secrets.close()
} }
if ('add' === subcommand || 'set' === subcommand) { if (subcommand === 'add' || subcommand === 'set') {
if (2 !== args.length) { if (args.length !== 2) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret add <name> <value>`')}`); error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret add <name> <value>`')}`)
if (args.length > 2) { if (args.length > 2) {
const [, ...rest] = args; const example = chalk.cyan(`$ now secret add ${args[0]}`)
console.log('> If your secret has spaces, make sure to wrap it in quotes. Example: \n' console.log(`> If your secret has spaces, make sure to wrap it in quotes. Example: \n ${example} `)
+ ` ${chalk.cyan('$ now secret add ${args[0]} "${escaped}"')} `);
} }
return exit(1);
return exit(1)
} }
const [name, value_] = args;
let value; const [name, value_] = args
let value
if (argv.base64) { if (argv.base64) {
value = { base64: value_ }; value = {base64: value_}
} else { } else {
value = value_; value = value_
} }
const secret = await secrets.add(name, value);
const elapsed = ms(new Date() - start); const secret = await secrets.add(name, value)
console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(name.toLowerCase())} ${chalk.gray(`(${secret.uid})`)} added ${chalk.gray(`[${elapsed}]`)}`); const elapsed = ms(new Date() - start)
return secrets.close();
console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(name.toLowerCase())} ${chalk.gray(`(${secret.uid})`)} added ${chalk.gray(`[${elapsed}]`)}`)
return secrets.close()
} }
error('Please specify a valid subcommand: ls | add | rename | rm'); error('Please specify a valid subcommand: ls | add | rename | rm')
help(); help()
exit(1); exit(1)
} }
process.on('uncaughtException', (err) => { process.on('uncaughtException', err => {
handleError(err); handleError(err)
exit(1); exit(1)
}); })
function readConfirmation(secret) { function readConfirmation(secret) {
return new Promise((resolve, reject) => { return new Promise(resolve => {
const time = chalk.gray(ms(new Date() - new Date(secret.created)) + ' ago'); const time = chalk.gray(ms(new Date() - new Date(secret.created)) + ' ago')
const tbl = table( const tbl = table(
[[secret.uid, chalk.bold(secret.name), time]], [[secret.uid, chalk.bold(secret.name), time]],
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} {align: ['l', 'r', 'l'], hsep: ' '.repeat(6)}
); )
process.stdout.write('> The following secret will be removed permanently\n'); process.stdout.write('> The following secret will be removed permanently\n')
process.stdout.write(' ' + tbl + '\n'); process.stdout.write(' ' + tbl + '\n')
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`); process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`)
process.stdin.on('data', (d) => { process.stdin.on('data', d => {
process.stdin.pause(); process.stdin.pause()
resolve('y' === d.toString().trim().toLowerCase()); resolve(d.toString().trim().toLowerCase() === 'y')
}).resume(); }).resume()
}); })
} }

4
package.json

@ -57,7 +57,9 @@
"no-use-before-define": 0, "no-use-before-define": 0,
"complexity": 0, "complexity": 0,
"unicorn/no-process-exit": 0, "unicorn/no-process-exit": 0,
"no-control-regex": 0 "no-control-regex": 0,
"no-case-declarations": 0,
"no-useless-escape": 0
} }
}, },
"bin": { "bin": {

Loading…
Cancel
Save