diff --git a/bin/now-alias b/bin/now-alias index 82e603e..593f54c 100755 --- a/bin/now-alias +++ b/bin/now-alias @@ -85,7 +85,6 @@ if (argv.help || !subcommand) { .then(async (token) => { try { await run(token); - exit(0); } catch (err) { if (err.userError) { error(err.message); @@ -108,6 +107,9 @@ async function run (token) { switch (subcommand) { case 'list': case 'ls': + const list = await alias.list(); + const urls = new Map(list.map(l => [l.uid, l.url])); + const target = null != args[0] ? String(args[0]) : null; const aliases = await alias.ls(target); const byTarget = new Map(); @@ -124,12 +126,12 @@ async function run (token) { const current = new Date(); const text = sorted.map(([target, _aliases]) => { - const t = table(_aliases.map((_alias) => { + return table(_aliases.map((_alias) => { const _url = chalk.underline(`https://${_alias.alias}`); + const _sourceUrl = chalk.underline(`https://${urls.get(target)}`); const time = chalk.gray(ms(current - new Date(_alias.created)) + ' ago'); - return [_alias.uid, _url, time]; - }), { align: ['l', 'r', 'l'], hsep: ' '.repeat(6) }); - return chalk.bold(target) + '\n\n' + indent(t, 2); + return [_alias.uid, _sourceUrl, _url, time]; + }), { align: ['l', 'r', 'l'], hsep: ' '.repeat(3) }); }).join('\n\n'); if (text) console.log('\n' + text + '\n'); @@ -193,6 +195,8 @@ async function run (token) { exit(1); } } + + alias.close(); } async function sort (aliases) { diff --git a/bin/now-remove b/bin/now-remove index 3f9495d..a0422de 100755 --- a/bin/now-remove +++ b/bin/now-remove @@ -55,13 +55,13 @@ function readConfirmation (app) { return new Promise((resolve, reject) => { const time = chalk.gray(ms(new Date() - app.created) + ' ago'); const tbl = table( - [[app.uid, time, `https://${app.url}`]], + [[app.uid, `https://${app.url}`, time]], { align: ['l', 'r', 'l'], hsep: ' '.repeat(6) } ); process.stdout.write('> The following deployment will be removed permanently\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.pause(); diff --git a/lib/alias.js b/lib/alias.js index de206f1..369346c 100644 --- a/lib/alias.js +++ b/lib/alias.js @@ -76,6 +76,8 @@ export default class Alias extends Now { } async set (deployment, alias) { + alias = alias.toLowerCase(); + const depl = await this.findDeployment(deployment); if (!depl) { const err = new Error(`Deployment not found by "${deployment}". Run ${chalk.dim('`now ls`')} to see your deployments.`); @@ -91,11 +93,21 @@ export default class Alias extends Now { alias = toHost(alias); } - await this.createAlias(depl, alias); - console.log(`${chalk.cyan('> Success!')} Set up alias ${chalk.bold(alias)}`); + if (!/\.now\.sh$/.test(alias)) { + console.log(`> ${chalk.bold(chalk.underline(alias))} is a custom domain.`); + console.log(`> Verifying that ${chalk.bold(chalk.underline(alias))} has a ${chalk.cyan('`CNAME`')} or ${chalk.cyan('`ALIAS`')} record pointing to ${chalk.bold(chalk.underline('alias.zeit.co'))}.`); + await this.verifyOwnership(alias); + } + + const { created, uid } = await this.createAlias(depl, alias); + if (created) { + console.log(`${chalk.cyan('> Success!')} Alias created ${chalk.dim(`(${uid})`)}: ${chalk.bold(`https://${depl.url}`)} ${chalk.dim(`(${depl.uid})`)} now points to ${chalk.bold(chalk.underline(`https://${alias}`))}`); + } else { + console.log(`${chalk.cyan('> Success!')} Alias already exists ${chalk.dim(`(${uid})`)}.`); + } } - async createAlias (depl, alias) { + createAlias (depl, alias) { return this.retry(async (bail, attempt) => { if (this._debug) console.time(`> [debug] /now/deployments/${depl.uid}/aliases #${attempt}`); const res = await this._fetch(`/now/deployments/${depl.uid}/aliases`, { @@ -103,23 +115,24 @@ export default class Alias extends Now { body: { alias } }); - if (304 === res.status) return; - const body = await res.json(); if (this._debug) console.timeEnd(`> [debug] /now/deployments/${depl.uid}/aliases #${attempt}`); + // 409 conflict is returned if it already exists + if (409 === res.status) return { uid: body.error.uid }; + // no retry on authorization problems if (403 === res.status) { const code = body.error.code; if ('custom_domain_needs_upgrade' === code) { - const err = new Error(`You are attempting to use a custom domain alias (${chalk.underline(chalk.cyan(alias))}), but this is only enabled for premium accounts. Please upgrade at ${chalk.underline('https://zeit.co/account')}`); + const err = new Error(`Custom domains are only enabled for premium accounts. Please upgrade at ${chalk.underline('https://zeit.co/account')}.`); err.userError = true; return bail(err); } if ('alias_in_use' === code) { - const err = new Error(`The alias you are trying to configure (${chalk.underline(chalk.cyan(alias))}) is already in use by a different account.`); + const err = new Error(`The alias you are trying to configure (${chalk.underline(chalk.bold(alias))}) is already in use by a different account.`); err.userError = true; return bail(err); } @@ -138,7 +151,6 @@ export default class Alias extends Now { if ('cert_missing' === code) { console.log(`> Provisioning certificate for ${chalk.underline(chalk.cyan(alias))}`); - await this.verifyOwnership(); await this.createCert(); // try again, but now having provisioned the certificate @@ -152,10 +164,12 @@ export default class Alias extends Now { if (200 !== res.status && 304 !== res.status) { throw new Error('Unhandled error'); } + + return body; }); } - async verifyOwnership (domain) { + verifyOwnership (domain) { return this.retry(async (bail, attempt) => { const targets = await resolve4('alias.zeit.co'); @@ -172,7 +186,7 @@ export default class Alias extends Now { for (const ip of ips) { if (!~targets.indexOf(ip)) { - const err = new Error(`The domain ${domain} has an A record ${chalk.bold(ip)} that doesn\'t resolve to ${chalk.bold('alias.zeit.co')}. Make sure the appropriate \`ALIAS\` or \`CNAME\` records are configured.`); + const err = new Error(`The domain ${domain} has an A record ${chalk.bold(ip)} that doesn\'t resolve to ${chalk.bold(chalk.underline('alias.zeit.co'))}. Please check your DNS settings.`); err.userError = true; return bail(err); } @@ -180,7 +194,7 @@ export default class Alias extends Now { }); } - async createCert (domain) { + createCert (domain) { return this.retry(async (bail, attempt) => { if (this._debug) console.time(`> [debug] /certs #${attempt}`); const res = await this._fetch('/certs', { diff --git a/package.json b/package.json index 3fe83d3..7f23088 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "ansi-escapes": "1.4.0", "arr-flatten": "1.0.1", "array-unique": "0.2.1", - "babel-runtime": "6.9.0", + "babel-runtime": "6.5.0", "bytes": "2.3.0", "chalk": "1.1.3", "copy-paste": "1.2.0",