You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

179 lines
4.2 KiB

// 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
})
}
}