UNPKG

nuxthub

Version:

Interface with the NuxtHub platform from the command line.

106 lines (90 loc) 3.59 kB
import { colors } from 'consola/utils' import dns2 from 'dns2' import { ofetch } from 'ofetch' import ora from 'ora' const TIMEOUT = 1000 * 60 * 5 const POLL_INTERVAL = 1000 const sleep = (ms) => new Promise((r) => setTimeout(r, ms)) // Inspired by https://github.com/cloudflare/workers-sdk/blob/b58ed9f2e7236e0e88f936bbf946f310ca3cf37f/packages/create-cloudflare/src/helpers/poll.ts#L20 export async function pollDns (url) { const start = Date.now() const s = ora().start('Waiting for DNS to propagate') const domain = new URL(url).host // Start out by sleeping for 10 seconds since it's unlikely DNS changes will await sleep(10 * 1000) while (Date.now() - start < TIMEOUT) { s.text =`Waiting for DNS to propagate (${secondsSince(start)}s)` if (await isDomainResolvable(domain)) { s.succeed(`DNS propagation ${colors.cyanBright('complete')}.`) return } await sleep(POLL_INTERVAL) } s.fail(`Timed out while waiting for ${colors.cyanBright(url)} - try accessing it in a few minutes.`) } export async function pollHttp (url) { const start = Date.now() const s = ora('Waiting for deployment to become available').start() while (Date.now() - start < TIMEOUT) { s.text = `Waiting for deployment to become available ${secondsSince(start) > 3 ? `(${secondsSince(start)}s)` : ''}` try { const response = await ofetch.raw(url, { reset: true, headers: { 'Cache-Control': 'no-cache' }, }) if (response.status < 300) { s.succeed(`Deployment is ready at ${colors.cyanBright(url)}`) return true } } catch (e) { if (e.response?.status === 401) { s.succeed(`Deployment is ready at ${colors.cyanBright(url)}`) return true } if (e.response && e.response.status !== 404) { s.fail(e.message) process.exit(1) } } await sleep(POLL_INTERVAL) } } // Determines if the domain is resolvable via DNS. Until this condition is true, // any HTTP requests will result in an NXDOMAIN error. export const isDomainResolvable = async (domain) => { try { const nameServers = await lookupSubdomainNameservers(domain) // If the subdomain nameservers aren't resolvable yet, keep polling if (nameServers.length === 0) return false // Once they are resolvable, query these nameservers for the domain's 'A' record const dns = new dns2({ nameServers }) const res = await dns.resolve(domain, 'A') return res.answers.length > 0 } catch { return false } } // Looks up the nameservers that are responsible for this particular domain export const lookupSubdomainNameservers = async (domain) => { const nameServers = await lookupDomainLevelNameservers(domain) const dns = new dns2({ nameServers }) const res = await dns.resolve(domain, 'NS') return ( res.authorities // Filter out non-authoritative authorities (ones that don't have an 'ns' property) .filter((r) => Boolean(r.ns)) // Return only the hostnames of the authoritative servers .map((r) => r.ns) ) } // Looks up the nameservers responsible for handling `pages.dev` or `workers.dev` domains export const lookupDomainLevelNameservers = async (domain) => { // Get the last 2 parts of the domain (ie. `pages.dev` or `workers.dev`) const baseDomain = domain.split('.').slice(-2).join('.') const dns = new dns2({}) const nameservers = await dns.resolve(baseDomain, 'NS') return (nameservers.answers).map((n) => n.ns) } function secondsSince(start) { return Math.round((Date.now() - start) / 1000) }