// Native const { encode: encodeQuery } = require('querystring') // Packages const chalk = require('chalk') // Ours const Now = require('../lib') const isZeitWorld = require('./is-zeit-world') const { DNS_VERIFICATION_ERROR } = require('./errors') const cmd = require('./utils/output/param') const domainRegex = /^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$/ module.exports = class Domains extends Now { async ls() { return this.listDomains() } async rm(name) { return this.retry(async (bail, attempt) => { if (this._debug) { console.time(`> [debug] #${attempt} DELETE /domains/${name}`) } const res = await this._fetch(`/domains/${name}`, { method: 'DELETE' }) if (this._debug) { console.timeEnd(`> [debug] #${attempt} DELETE /domains/${name}`) } if (res.status === 403) { return bail(new Error('Unauthorized')) } if (res.status !== 200) { const body = await res.json() throw new Error(body.error.message) } }) } async add(domain, skipVerification, isExternal) { if (!domainRegex.test(domain)) { const err = new Error( `The supplied value ${chalk.bold(`"${domain}"`)} is not a valid domain.` ) err.userError = true throw err } if (skipVerification || isExternal) { return this.setupDomain(domain, { isExternal }) } let ns try { console.log('> Verifying nameservers…') const res = await this.getNameservers(domain) ns = res.nameservers } catch (err) { const err2 = new Error( `Unable to fetch nameservers for ${chalk.underline(chalk.bold(domain))}.` ) err2.userError = true throw err2 } if (isZeitWorld(ns)) { console.log(`> Verification ${chalk.bold('OK')}!`) return this.setupDomain(domain) } if (this._debug) { console.log( `> [debug] Supplied domain "${domain}" has non-zeit nameservers` ) } const err3 = new Error(DNS_VERIFICATION_ERROR) err3.userError = true throw err3 } async status(name) { if (!name) { throw new Error('`domain` is not defined') } const query = encodeQuery({ name }) return this.retry(async (bail, attempt) => { if (this._debug) { console.time(`> [debug] #${attempt} GET /domains/status?${query}`) } const res = await this._fetch(`/domains/status?${query}`) const json = await res.json() if (this._debug) { console.timeEnd(`> [debug] #${attempt} GET /domains/status?${query}`) } return json.available }) } async price(name) { if (!name) { throw new Error('`domain` is not defined') } const query = encodeQuery({ name }) return this.retry(async (bail, attempt) => { if (this._debug) { console.time(`> [debug] #${attempt} GET /domains/price?${query}`) } const res = await this._fetch(`/domains/price?${query}`) const json = await res.json() if (res.status === 400) { const e = new Error(json.error.message) e.code = json.error.code return bail(e) } if (this._debug) { console.timeEnd(`> [debug] #${attempt} GET /domains/price?${query}`) } return json }) } async buy(name) { if (!name) { throw new Error('`name` is not defined') } const rawBody = { name } if (name.startsWith('test')) { rawBody.dev = true } const body = JSON.stringify(rawBody) return this.retry(async (bail, attempt) => { if (this._debug) { console.time(`> [debug] #${attempt} GET /domains/buy`) } const res = await this._fetch(`/domains/buy`, { method: 'POST', body }) const json = await res.json() if (this._debug) { console.timeEnd(`> [debug] #${attempt} GET /domains/buy`) } if ([400, 403, 500, 503].includes(res.status)) { const e = new Error() e.code = json.error.code if (json.error.code === 'source_not_found') { e.message = `No credit cards found – please run ${cmd('now cc add')}` } else { e.message = json.error.message } return bail(e) } return json }) } }