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.

774 lines
20 KiB

// Native
const { homedir } = require("os");
const { resolve: resolvePath, join: joinPaths } = require("path");
const EventEmitter = require("events");
// Packages
const bytes = require("bytes");
const chalk = require("chalk");
const resumer = require("resumer");
const retry = require("async-retry");
const splitArray = require("split-array");
const { parse: parseIni } = require("ini");
const { readFile, stat, lstat } = require("fs-promise");
// Ours
const { npm: getNpmFiles, docker: getDockerFiles } = require("./get-files");
const ua = require("./ua");
const hash = require("./hash");
const Agent = require("./agent");
const readMetaData = require("./read-metadata");
9 years ago
// how many concurrent HTTP/2 stream uploads
const MAX_CONCURRENT = 10;
// check if running windows
const IS_WIN = /^win/.test(process.platform);
const SEP = IS_WIN ? "\\" : "/";
module.exports = class Now extends EventEmitter {
constructor(url, token, { forceNew = false, debug = false }) {
super();
this._token = token;
this._debug = debug;
this._forceNew = forceNew;
this._agent = new Agent(url, { debug });
this._onRetry = this._onRetry.bind(this);
9 years ago
}
async create(
path,
{
wantsPublic,
quiet = false,
env = {},
followSymlinks = true,
forceNew = false,
forceSync = false,
forwardNpm = false,
deploymentType = "npm",
deploymentName,
isStatic = false
}
) {
this._path = path;
this._static = isStatic;
let files;
const meta = await readMetaData(path, {
deploymentType,
deploymentName,
quiet,
isStatic
});
const { pkg, name, description, nowConfig, hasNowJson } = meta;
deploymentType = meta.deploymentType;
if (this._debug) {
console.time("> [debug] Getting files");
}
const opts = { debug: this._debug, hasNowJson };
if (deploymentType === "npm") {
files = await getNpmFiles(path, pkg, nowConfig, opts);
} else {
files = await getDockerFiles(path, nowConfig, opts);
}
if (this._debug) {
console.timeEnd("> [debug] Getting files");
}
forwardNpm = forwardNpm || (nowConfig && nowConfig.forwardNpm);
// Read .npmrc
let npmrc = {};
let authToken;
if (deploymentType === "npm" && forwardNpm) {
try {
npmrc = await readFile(resolvePath(path, ".npmrc"), "utf8");
npmrc = parseIni(npmrc);
authToken = npmrc["//registry.npmjs.org/:_authToken"];
} catch (err) {
// Do nothing
}
if (!authToken) {
try {
npmrc = await readFile(resolvePath(homedir(), ".npmrc"), "utf8");
npmrc = parseIni(npmrc);
authToken = npmrc["//registry.npmjs.org/:_authToken"];
} catch (err) {
// Do nothing
}
}
}
if (this._debug) {
console.time("> [debug] Computing hashes");
}
const pkgDetails = {};
pkgDetails.name = name;
Object.assign(pkgDetails, pkg);
const hashes = await hash(files, isStatic, pkgDetails);
if (this._debug) {
console.timeEnd("> [debug] Computing hashes");
}
this._files = hashes;
const engines = (nowConfig && nowConfig.engines) || pkg.engines;
const deployment = await this.retry(async bail => {
if (this._debug) {
console.time("> [debug] /now/create");
}
// Flatten the array to contain files to sync where each nested input
// array has a group of files with the same sha but different path
const files = await Promise.all(
Array.prototype.concat.apply(
[],
await Promise.all(
Array.from(this._files).map(async ([sha, { data, names }]) => {
const statFn = followSymlinks ? stat : lstat;
return await names.map(async name => {
let mode;
const getMode = async () => {
const st = await statFn(name);
return st.mode;
};
if (this._static) {
if (toRelative(name, this._path) === "package.json") {
mode = 33261;
} else {
mode = await getMode();
name = this.pathInsideContent(name);
}
} else {
mode = await getMode();
}
return {
sha,
size: data.length,
file: toRelative(name, this._path),
mode
};
});
})
)
)
);
const res = await this._fetch("/now/create", {
method: "POST",
body: {
env,
public: wantsPublic,
forceNew,
forceSync,
name,
description,
deploymentType,
registryAuthToken: authToken,
files,
engines
}
});
if (this._debug) {
console.timeEnd("> [debug] /now/create");
}
// no retry on 4xx
let body;
try {
body = await res.json();
} catch (err) {
throw new Error("Unexpected response");
}
if (res.status === 429) {
let msg = `You reached your 20 deployments limit in the OSS plan.\n`;
msg += `${chalk.gray(">")} Please run ${chalk.gray("`")}${chalk.cyan("now upgrade")}${chalk.gray("`")} to proceed`;
const err = new Error(msg);
err.status = res.status;
err.retryAfter = "never";
return bail(err);
} else if (res.status >= 400 && res.status < 500) {
const err = new Error(body.error.message);
err.userError = true;
return bail(err);
} else if (res.status !== 200) {
throw new Error(body.error.message);
}
return body;
});
// we report about files whose sizes are too big
let missingVersion = false;
if (deployment.warnings) {
let sizeExceeded = 0;
deployment.warnings.forEach(warning => {
if (warning.reason === "size_limit_exceeded") {
const { sha, limit } = warning;
const n = hashes.get(sha).names.pop();
console.error(
"> \u001b[31mWarning!\u001b[39m Skipping file %s (size exceeded %s)",
n,
bytes(limit)
);
hashes.get(sha).names.unshift(n); // move name (hack, if duplicate matches we report them in order)
sizeExceeded++;
} else if (warning.reason === "node_version_not_found") {
const { wanted, used } = warning;
console.error(
"> \u001b[31mWarning!\u001b[39m Requested node version %s is not available",
wanted,
used
);
missingVersion = true;
}
});
if (sizeExceeded) {
console.error(
`> \u001b[31mWarning!\u001b[39m ${sizeExceeded} of the files ` +
"exceeded the limit for your plan.\n" +
`> Please run ${chalk.gray("`")}${chalk.cyan("now upgrade")}${chalk.gray("`")} to upgrade.`
);
}
}
if (!quiet && deploymentType === "npm" && deployment.nodeVersion) {
if (engines && engines.node) {
if (missingVersion) {
console.log(
`> Using Node.js ${chalk.bold(deployment.nodeVersion)} (default)`
);
} else {
console.log(
`> Using Node.js ${chalk.bold(deployment.nodeVersion)} (requested: ${chalk.dim(`\`${engines.node}\``)})`
);
}
} else {
console.log(
`> Using Node.js ${chalk.bold(deployment.nodeVersion)} (default)`
);
}
}
this._id = deployment.deploymentId;
this._host = deployment.url;
this._missing = deployment.missing || [];
return this._url;
}
pathInsideContent(position) {
const relativePath = toRelative(position, this._path);
const contentDir = joinPaths(this._path, "content");
const newPath = joinPaths(contentDir, relativePath);
return newPath;
}
upload() {
const parts = splitArray(this._missing, MAX_CONCURRENT);
9 years ago
if (this._debug) {
console.log(
"> [debug] Will upload " +
`${this._missing.length} files in ${parts.length} ` +
`steps of ${MAX_CONCURRENT} uploads.`
);
}
9 years ago
const uploadChunk = () => {
Promise.all(
parts.shift().map(sha =>
retry(
async (bail, attempt) => {
const file = this._files.get(sha);
const { data, names } = file;
if (this._debug) {
console.time(`> [debug] /sync #${attempt} ${names.join(" ")}`);
}
const stream = resumer().queue(data).end();
const res = await this._fetch("/now/sync", {
method: "POST",
headers: {
"Content-Type": "application/octet-stream",
"Content-Length": data.length,
"x-now-deployment-id": this._id,
"x-now-sha": sha,
"x-now-file": names
.map(name => {
if (this._static) {
name = this.pathInsideContent(name);
}
return toRelative(encodeURIComponent(name), this._path);
})
.join(","),
"x-now-size": data.length
},
body: stream
});
if (this._debug) {
console.timeEnd(
`> [debug] /sync #${attempt} ${names.join(" ")}`
);
}
// no retry on 4xx
if (
res.status !== 200 && (res.status >= 400 || res.status < 500)
) {
if (this._debug) {
console.log(
"> [debug] bailing on creating due to %s",
res.status
);
}
return bail(responseError(res));
}
9 years ago
this.emit("upload", file);
},
{ retries: 3, randomize: true, onRetry: this._onRetry }
))
)
.then(() => parts.length ? uploadChunk() : this.emit("complete"))
.catch(err => this.emit("error", err));
};
9 years ago
uploadChunk();
9 years ago
}
async listSecrets() {
return this.retry(async (bail, attempt) => {
if (this._debug) {
console.time(`> [debug] #${attempt} GET /secrets`);
}
const res = await this._fetch("/now/secrets");
if (this._debug) {
console.timeEnd(`> [debug] #${attempt} GET /secrets`);
}
const body = await res.json();
return body.secrets;
});
}
async list(app) {
const query = app ? `?app=${encodeURIComponent(app)}` : "";
const { deployments } = await this.retry(
async bail => {
if (this._debug) {
console.time("> [debug] /list");
}
const res = await this._fetch("/now/list" + query);
if (this._debug) {
console.timeEnd("> [debug] /list");
}
// no retry on 4xx
if (res.status >= 400 && res.status < 500) {
if (this._debug) {
console.log("> [debug] bailing on listing due to %s", res.status);
}
return bail(responseError(res));
}
if (res.status !== 200) {
throw new Error("Fetching deployment list failed");
}
return res.json();
},
{ retries: 3, minTimeout: 2500, onRetry: this._onRetry }
);
return deployments;
}
async listAliases(deploymentId) {
return this.retry(async () => {
const res = await this._fetch(
deploymentId
? `/now/deployments/${deploymentId}/aliases`
: "/now/aliases"
);
const body = await res.json();
return body.aliases;
});
}
async last(app) {
const deployments = await this.list(app);
const last = deployments
.sort((a, b) => {
return b.created - a.created;
})
.shift();
if (!last) {
const e = Error(`No deployments found for "${app}"`);
e.userError = true;
throw e;
}
return last;
}
async listDomains() {
return this.retry(async (bail, attempt) => {
if (this._debug) {
console.time(`> [debug] #${attempt} GET /domains`);
}
const res = await this._fetch("/domains");
if (this._debug) {
console.timeEnd(`> [debug] #${attempt} GET /domains`);
}
const body = await res.json();
return body.domains;
});
}
async getDomain(domain) {
return this.retry(async (bail, attempt) => {
if (this._debug) {
console.time(`> [debug] #${attempt} GET /domains/${domain}`);
}
const res = await this._fetch(`/domains/${domain}`);
if (this._debug) {
console.timeEnd(`> [debug] #${attempt} GET /domains/${domain}`);
}
return await res.json();
});
}
getNameservers(domain) {
return new Promise((resolve, reject) => {
let fallback = false;
this.retry(async (bail, attempt) => {
if (this._debug) {
console.time(
`> [debug] #${attempt} GET /whois-ns${fallback ? "-fallback" : ""}`
);
}
const res = await this._fetch(
`/whois-ns${fallback ? "-fallback" : ""}?domain=${encodeURIComponent(domain)}`
);
if (this._debug) {
console.timeEnd(
`> [debug] #${attempt} GET /whois-ns${fallback ? "-fallback" : ""}`
);
}
const body = await res.json();
if (res.status === 200) {
if (
(!body.nameservers || body.nameservers.length === 0) && !fallback
) {
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
// if the nameservers are `null` it's likely
// that our whois service failed to parse it
fallback = true;
throw new Error("Invalid whois response");
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
}
return body;
}
if (attempt > 1) {
fallback = true;
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
}
throw new Error(`Whois error (${res.status}): ${body.error.message}`);
})
.then(body => {
body.nameservers = body.nameservers.filter(ns => {
// temporary hack:
// sometimes we get a response that looks like:
// ['ns', 'ns', '', '']
// so we filter the empty ones
return ns.length;
});
resolve(body);
})
.catch(err => {
reject(err);
});
});
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
}
// _ensures_ the domain is setup (idempotent)
setupDomain(name, { isExternal } = {}) {
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
return this.retry(async (bail, attempt) => {
if (this._debug) {
console.time(`> [debug] #${attempt} POST /domains`);
}
const res = await this._fetch("/domains", {
method: "POST",
body: { name, isExternal: Boolean(isExternal) }
});
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
if (this._debug) {
console.timeEnd(`> [debug] #${attempt} POST /domains`);
}
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
const body = await res.json();
if (res.status === 403) {
const code = body.error.code;
let err;
if (code === "custom_domain_needs_upgrade") {
err = new Error(
`Custom domains are only enabled for premium accounts. Please upgrade at ${chalk.underline("https://zeit.co/account")}.`
);
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
} else {
err = new Error(`Not authorized to access domain ${name}`);
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
}
err.userError = true;
return bail(err);
} else if (res.status === 409) {
// domain already exists
if (this._debug) {
console.log("> [debug] Domain already exists (noop)");
}
return { uid: body.error.uid, code: body.error.code };
} else if (
res.status === 401 &&
body.error &&
body.error.code === "verification_failed"
) {
const err = new Error(body.error.message);
err.userError = true;
return bail(err);
} else if (res.status !== 200) {
throw new Error(body.error.message);
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
}
return body;
});
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
}
createCert(domain, { renew } = {}) {
return this.retry(
async (bail, attempt) => {
if (this._debug) {
console.time(`> [debug] /now/certs #${attempt}`);
}
const res = await this._fetch("/now/certs", {
method: "POST",
body: {
domains: [domain],
renew
}
});
if (res.status === 304) {
console.log("> Certificate already issued.");
return;
}
const body = await res.json();
if (this._debug) {
console.timeEnd(`> [debug] /now/certs #${attempt}`);
}
if (body.error) {
const { code } = body.error;
if (code === "verification_failed") {
const err = new Error(
"The certificate issuer failed to verify ownership of the domain. " +
"This likely has to do with DNS propagation and caching issues. Please retry later!"
);
err.userError = true;
// retry
throw err;
} else if (code === "rate_limited") {
const err = new Error(body.error.message);
err.userError = true;
// dont retry
return bail(err);
}
throw new Error(body.error.message);
}
if (res.status !== 200 && res.status !== 304) {
throw new Error("Unhandled error");
}
return body;
},
{ retries: 5, minTimeout: 30000, maxTimeout: 90000 }
);
}
deleteCert(domain) {
return this.retry(async (bail, attempt) => {
if (this._debug) {
console.time(`> [debug] /now/certs #${attempt}`);
}
const res = await this._fetch(`/now/certs/${domain}`, {
method: "DELETE"
});
if (res.status !== 200) {
const err = new Error(res.body.error.message);
err.userError = false;
if (res.status === 400 || res.status === 404) {
return bail(err);
}
throw err;
}
});
}
async remove(deploymentId, { hard }) {
const data = { deploymentId, hard };
await this.retry(async bail => {
if (this._debug) {
console.time("> [debug] /remove");
}
9 years ago
const res = await this._fetch("/now/remove", {
method: "DELETE",
9 years ago
body: data
});
if (this._debug) {
console.timeEnd("> [debug] /remove");
}
// no retry on 4xx
if (res.status >= 400 && res.status < 500) {
if (this._debug) {
console.log("> [debug] bailing on removal due to %s", res.status);
}
return bail(responseError(res));
}
if (res.status !== 200) {
throw new Error("Removing deployment failed");
}
});
return true;
}
retry(fn, { retries = 3, maxTimeout = Infinity } = {}) {
return retry(fn, {
Add zeit world (wip) (#67) * alias: clean up the alias (trailing and leading dots) * alias: improve domain validation and implement zeit.world * is-zeit-world: detect valid zeit.world nameservers * package: add domain-regex dep * now-alias: fix edge case with older aliases or removed deployments * alias: move listing aliases and retrying to `index` * index: generalize retrying and alias listing * alias: remove `retry` dep * now-remove: print out alias warning * typo * now-alias: prevent double lookup * now: add domain / domains command * now-deploy: document `domain` * agent: allow for tls-less, agent-less requests while testing * is-zeit-world: fix nameserver typo * dns: as-promised * now-alias: fix `rm` table * now-alias: no longer admit argument after `alias ls` @rase- please verify this, but I think it was overkill? * admit &#34;aliases&#34; as an alternative to alias * make domain name resolution, creation and verification reusable * index: add nameserver discover, domain setup functions (reused between alias and domains) * now-domains: add command * domains: commands * package: bump eslint * now-alias: simplify sort * now-domains: sort list * now-domains: improve deletion and output of empty elements * errors: improve output * domains: add more debug output * domains: extra logging * errors: improve logging * now-remove: improve `now rm` error handling * index: more reasonable retrying * alias: support empty dns configurations * dns: remove ns fn * alias: improve post-dns-editing verification * index: remove unneeded dns lookup * index: implement new `getNameservers` * index: customizable retries * alias: improve error * improve error handling and cert retrying * customizable retries * alias: better error handling * alias: display both error messages * better error handling * improve error handling for certificate verification errors * alias: set up a `*` CNAME to simplify further aliases * alias: fewer retries for certs, more spaced out (prevent rate limiting issues) * alias: ultimate error handling * add whois fallback * adjust timer labels for whois fallback * index: whois fallback also upon 500 errors * alias: fix error message * fix duplicate aliases declaration
9 years ago
retries,
maxTimeout,
onRetry: this._onRetry
});
}
_onRetry(err) {
if (this._debug) {
console.log(`> [debug] Retrying: ${err}\n${err.stack}`);
}
}
close() {
this._agent.close();
}
get id() {
return this._id;
}
get url() {
return `https://${this._host}`;
}
get host() {
return this._host;
}
get syncAmount() {
9 years ago
if (!this._syncAmount) {
this._syncAmount = this._missing
.map(sha => this._files.get(sha).data.length)
.reduce((a, b) => a + b, 0);
}
return this._syncAmount;
}
_fetch(_url, opts = {}) {
opts.headers = opts.headers || {};
opts.headers.authorization = `Bearer ${this._token}`;
opts.headers["user-agent"] = ua;
return this._agent.fetch(_url, opts);
}
};
function toRelative(path, base) {
const fullBase = base.endsWith(SEP) ? base : base + SEP;
let relative = path.substr(fullBase.length);
if (relative.startsWith(SEP)) {
relative = relative.substr(1);
}
return relative.replace(/\\/g, "/");
}
function responseError(res) {
const err = new Error("Response error");
err.status = res.status;
if (res.status === 429) {
const retryAfter = res.headers.get("Retry-After");
if (retryAfter) {
err.retryAfter = parseInt(retryAfter, 10);
}
}
return err;
9 years ago
}