Browse Source

alias: complete `set`

master
Guillermo Rauch 9 years ago
parent
commit
f5c5309bc8
  1. 198
      lib/alias.js

198
lib/alias.js

@ -1,47 +1,16 @@
import retry from 'async-retry';
import dns from 'dns';
import Now from '../lib';
import toHost from './to-host';
import chalk from 'chalk';
export default class Alias extends Now {
async ls () {
return retry(async (bail) => {
const res = await this._fetch('/now/aliases');
if (200 !== res.status && (400 <= res.status || 500 > res.status)) {
if (this._debug) console.log('> [debug] bailing on creating due to %s', res.status);
return bail(responseError(res));
}
return await res.json();
}, { retries: 3, minTimeout: 2500, onRetry: this._onRetry });
}
async rm (url, aliases) {
console.log('rm', url, aliases);
const deploymentId = url; // TODO get from API
return await Promise.all(aliases.map(async (alias) => {
retry(async (bail) => {
const res = await this._fetch(`/now/aliases/${deploymentId}/${alias}`, {
method: 'DELETE'
});
if (200 !== res.status && (400 <= res.status || 500 > res.status)) {
if (this._debug) console.log('> [debug] bailing on creating due to %s', res.status);
return bail(responseError(res));
}
return await res.json();
}, { retries: 3, minTimeout: 2500, onRetry: this._onRetry });
}));
}
async set (deployment, alias) {
const list = await this.list();
let key, val;
if (~deployment.indexOf('.')) {
if (/\./.test(deployment)) {
val = toHost(deployment);
key = 'url';
} else {
@ -49,31 +18,164 @@ export default class Alias extends Now {
key = 'uid';
}
const id = list.find((d) => {
return d[key] === val || // match entire host / uid
// match prefix
val + '.now.sh' === d.url;
const depl = list.find((d) => {
if (d[key] === val) {
if (this._debug) console.log(`> [debug] matched deployment ${d.uid} by ${key} ${val}`);
return true;
}
// match prefix
if (`${val}.now.sh` === d.url) {
if (this._debug) console.log(`> [debug] matched deployment ${d.uid} by url ${d.url}`);
return true;
}
return false;
});
if (!id) {
if (!depl) {
const err = new Error(`Deployment not found by "${deployment}". Run ${chalk.dim('`now ls`')} to see your deployments.`);
err.userError = true;
throw err;
}
// evaluate the alias
if (!/\./.test(alias)) {
if (this._debug) console.log(`> [debug] suffixing \`.now.sh\` to alias ${alias}`);
alias = `${alias}.now.sh`;
} else {
alias = toHost(alias);
}
const id = await this.createAlias(depl, alias);
console.log(`${chalk.cyan('> Success!')} Set up alias ${chalk.bold(id)}`);
}
}
async createAlias (depl, alias) {
return this.retry(async (bail, attempt) => {
if (this._debug) console.time(`> [debug] /now/deployments/${depl.uid}/aliases #${attempt}`);
const res = this._fetch(`/now/deployments/${depl.uid}/aliases`, {
method: 'POST',
body: { alias }
});
function responseError (res) {
const err = new Error('Response error');
err.status = res.status;
const body = await res.json();
if (this._debug) console.timeEnd(`> [debug] /now/deployments/${depl.uid}/aliases #${attempt}`);
if (429 === res.status) {
const retryAfter = res.headers.get('Retry-After');
if (retryAfter) {
err.retryAfter = parseInt(retryAfter, 10);
}
// 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')}`);
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.`);
err.userError = true;
return bail(err);
}
return bail(new Error('Authorization error'));
}
// all other errors
if (body.error) {
const code = body.error.code;
if ('deployment_not_found' === code) {
return bail(new Error('Deployment not found'));
}
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
return this.createAlias(depl, alias);
}
return bail(new Error(body.error.message));
}
// the two expected succesful cods are 200 and 304
if (200 !== res.status && 304 !== res.status) {
throw new Error('Unhandled error');
}
});
}
async verifyOwnership (domain) {
return this.retry(async (bail, attempt) => {
const targets = await resolve4('alias.zeit.co');
if (!targets.length) {
return bail(new Error('Unable to resolve alias.zeit.co'));
}
const ips = await resolve4(domain);
if (!ips.length) {
const err = new Error('The domain ${domain} A record in the DNS configuration is not returning any IPs.');
err.userError = true;
return bail(err);
}
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.`);
err.userError = true;
return bail(err);
}
}
});
}
async createCert (domain) {
return this.retry(async (bail, attempt) => {
if (this._debug) console.time(`> [debug] /certs #${attempt}`);
const res = this._fetch('/certs', {
method: 'POST',
body: {
domains: [domain]
}
});
const body = await res.json();
if (this._debug) console.timeEnd(`> [debug] /certs #${attempt}`);
if (body.error) {
const { code } = body.error;
if ('verification_failed' === code) {
const err = new Error(`We couldn't verify ownership of the domain ${domain}. Make sure the appropriate \`ALIAS\` or \`CNAME\` records are configured and pointing to ${chalk.bold('alias.zeit.co')}.`);
err.userError = true;
return bail(err);
}
throw new Error(body.message);
}
if (200 !== res.status && 304 !== res.status) {
throw new Error('Unhandled error');
}
});
}
retry (fn) {
return retry(fn, { retries: 5, randomize: true, onRetry: this._onRetry });
}
return err;
}
function resolve4 (host) {
return new Promise((resolve, reject) => {
return dns.resolve4(host, (err, answer) => {
if (err) return reject(err);
resolve(answer);
});
});
}

Loading…
Cancel
Save