Leo Lamprecht
8 years ago
61 changed files with 4236 additions and 3656 deletions
@ -1,345 +1,397 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
// Packages
|
|||
const chalk = require('chalk') |
|||
const minimist = require('minimist') |
|||
const table = require('text-table') |
|||
const ms = require('ms') |
|||
const chalk = require("chalk"); |
|||
const minimist = require("minimist"); |
|||
const table = require("text-table"); |
|||
const ms = require("ms"); |
|||
|
|||
// Ours
|
|||
const strlen = require('../lib/strlen') |
|||
const NowAlias = require('../lib/alias') |
|||
const login = require('../lib/login') |
|||
const cfg = require('../lib/cfg') |
|||
const {error} = require('../lib/error') |
|||
const toHost = require('../lib/to-host') |
|||
const {reAlias} = require('../lib/re-alias') |
|||
const exit = require('../lib/utils/exit') |
|||
const logo = require('../lib/utils/output/logo') |
|||
const promptBool = require('../lib/utils/input/prompt-bool') |
|||
const strlen = require("../lib/strlen"); |
|||
const NowAlias = require("../lib/alias"); |
|||
const login = require("../lib/login"); |
|||
const cfg = require("../lib/cfg"); |
|||
const { error } = require("../lib/error"); |
|||
const toHost = require("../lib/to-host"); |
|||
const { reAlias } = require("../lib/re-alias"); |
|||
const exit = require("../lib/utils/exit"); |
|||
const logo = require("../lib/utils/output/logo"); |
|||
const promptBool = require("../lib/utils/input/prompt-bool"); |
|||
|
|||
const argv = minimist(process.argv.slice(2), { |
|||
string: ['config', 'token', 'rules'], |
|||
boolean: ['help', 'debug'], |
|||
string: ["config", "token", "rules"], |
|||
boolean: ["help", "debug"], |
|||
alias: { |
|||
help: 'h', |
|||
config: 'c', |
|||
rules: 'r', |
|||
debug: 'd', |
|||
token: 't' |
|||
help: "h", |
|||
config: "c", |
|||
rules: "r", |
|||
debug: "d", |
|||
token: "t" |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
const subcommand = argv._[0] |
|||
const subcommand = argv._[0]; |
|||
|
|||
// options
|
|||
const help = () => { |
|||
console.log(` |
|||
console.log( |
|||
` |
|||
${chalk.bold(`${logo} now alias`)} <ls | set | rm> <deployment> <alias> |
|||
|
|||
${chalk.dim('Options:')} |
|||
${chalk.dim("Options:")} |
|||
|
|||
-h, --help Output usage information |
|||
-c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file |
|||
-r ${chalk.bold.underline('RULES_FILE')}, --rules=${chalk.bold.underline('RULES_FILE')} Rules file |
|||
-c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file |
|||
-r ${chalk.bold.underline("RULES_FILE")}, --rules=${chalk.bold.underline("RULES_FILE")} Rules file |
|||
-d, --debug Debug mode [off] |
|||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token |
|||
-t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token |
|||
|
|||
${chalk.dim('Examples:')} |
|||
${chalk.dim("Examples:")} |
|||
|
|||
${chalk.gray('–')} Lists all your aliases: |
|||
${chalk.gray("–")} Lists all your aliases: |
|||
|
|||
${chalk.cyan('$ now alias ls')} |
|||
${chalk.cyan("$ now alias ls")} |
|||
|
|||
${chalk.gray('–')} Adds a new alias to ${chalk.underline('my-api.now.sh')}: |
|||
${chalk.gray("–")} Adds a new alias to ${chalk.underline("my-api.now.sh")}: |
|||
|
|||
${chalk.cyan(`$ now alias set ${chalk.underline('api-ownv3nc9f8.now.sh')} ${chalk.underline('my-api.now.sh')}`)} |
|||
${chalk.cyan(`$ now alias set ${chalk.underline("api-ownv3nc9f8.now.sh")} ${chalk.underline("my-api.now.sh")}`)} |
|||
|
|||
The ${chalk.dim('`.now.sh`')} suffix can be ommited: |
|||
The ${chalk.dim("`.now.sh`")} suffix can be ommited: |
|||
|
|||
${chalk.cyan('$ now alias set api-ownv3nc9f8 my-api')} |
|||
${chalk.cyan("$ now alias set api-ownv3nc9f8 my-api")} |
|||
|
|||
The deployment id can be used as the source: |
|||
|
|||
${chalk.cyan('$ now alias set deploymentId my-alias')} |
|||
${chalk.cyan("$ now alias set deploymentId my-alias")} |
|||
|
|||
Custom domains work as alias targets: |
|||
|
|||
${chalk.cyan(`$ now alias set ${chalk.underline('api-ownv3nc9f8.now.sh')} ${chalk.underline('my-api.com')}`)} |
|||
${chalk.cyan(`$ now alias set ${chalk.underline("api-ownv3nc9f8.now.sh")} ${chalk.underline("my-api.com")}`)} |
|||
|
|||
${chalk.dim('–')} The subcommand ${chalk.dim('`set`')} is the default and can be skipped. |
|||
${chalk.dim('–')} ${chalk.dim('`http(s)://`')} in the URLs is unneeded / ignored. |
|||
${chalk.dim("–")} The subcommand ${chalk.dim("`set`")} is the default and can be skipped. |
|||
${chalk.dim("–")} ${chalk.dim("`http(s)://`")} in the URLs is unneeded / ignored. |
|||
|
|||
${chalk.gray('–')} Add and modify path based aliases for ${chalk.underline('zeit.ninja')}: |
|||
${chalk.gray("–")} Add and modify path based aliases for ${chalk.underline("zeit.ninja")}: |
|||
|
|||
${chalk.cyan(`$ now alias ${chalk.underline('zeit.ninja')} -r ${chalk.underline('rules.json')}`)} |
|||
${chalk.cyan(`$ now alias ${chalk.underline("zeit.ninja")} -r ${chalk.underline("rules.json")}`)} |
|||
|
|||
Export effective routing rules: |
|||
|
|||
${chalk.cyan(`$ now alias ls aliasId --json > ${chalk.underline('rules.json')}`)} |
|||
${chalk.cyan(`$ now alias ls aliasId --json > ${chalk.underline("rules.json")}`)} |
|||
|
|||
${chalk.cyan(`$ now alias ls zeit.ninja`)} |
|||
|
|||
${chalk.gray('–')} Removing an alias: |
|||
${chalk.gray("–")} Removing an alias: |
|||
|
|||
${chalk.cyan('$ now alias rm aliasId')} |
|||
${chalk.cyan("$ now alias rm aliasId")} |
|||
|
|||
To get the list of alias ids, use ${chalk.dim('`now alias ls`')}. |
|||
To get the list of alias ids, use ${chalk.dim("`now alias ls`")}. |
|||
|
|||
${chalk.dim('Alias:')} ln |
|||
`)
|
|||
} |
|||
${chalk.dim("Alias:")} ln |
|||
` |
|||
); |
|||
}; |
|||
|
|||
// options
|
|||
const debug = argv.debug |
|||
const apiUrl = argv.url || 'https://api.zeit.co' |
|||
const debug = argv.debug; |
|||
const apiUrl = argv.url || "https://api.zeit.co"; |
|||
|
|||
if (argv.config) { |
|||
cfg.setConfigFile(argv.config) |
|||
cfg.setConfigFile(argv.config); |
|||
} |
|||
|
|||
if (argv.help) { |
|||
help() |
|||
exit(0) |
|||
help(); |
|||
exit(0); |
|||
} else { |
|||
const config = cfg.read() |
|||
const config = cfg.read(); |
|||
|
|||
Promise.resolve(argv.token || config.token || login(apiUrl)) |
|||
.then(async token => { |
|||
try { |
|||
await run(token) |
|||
} catch (err) { |
|||
if (err.userError) { |
|||
error(err.message) |
|||
} else { |
|||
error(`Unknown error: ${err}\n${err.stack}`) |
|||
.then(async token => { |
|||
try { |
|||
await run(token); |
|||
} catch (err) { |
|||
if (err.userError) { |
|||
error(err.message); |
|||
} else { |
|||
error(`Unknown error: ${err}\n${err.stack}`); |
|||
} |
|||
exit(1); |
|||
} |
|||
exit(1) |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`) |
|||
exit(1) |
|||
}) |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`); |
|||
exit(1); |
|||
}); |
|||
} |
|||
|
|||
async function run(token) { |
|||
const alias = new NowAlias(apiUrl, token, {debug}) |
|||
const args = argv._.slice(1) |
|||
const alias = new NowAlias(apiUrl, token, { debug }); |
|||
const args = argv._.slice(1); |
|||
|
|||
switch (subcommand) { |
|||
case 'ls': |
|||
case 'list': { |
|||
case "ls": |
|||
case "list": { |
|||
if (args.length === 1) { |
|||
const list = await alias.listAliases() |
|||
const item = list.find(e => e.uid === argv._[1] || e.alias === argv._[1]) |
|||
const list = await alias.listAliases(); |
|||
const item = list.find( |
|||
e => e.uid === argv._[1] || e.alias === argv._[1] |
|||
); |
|||
if (!item || !item.rules) { |
|||
error(`Could not match path alias for: ${argv._[1]}`) |
|||
return exit(1) |
|||
error(`Could not match path alias for: ${argv._[1]}`); |
|||
return exit(1); |
|||
} |
|||
|
|||
if (argv.json) { |
|||
console.log(JSON.stringify({rules: item.rules}, null, 2)) |
|||
console.log(JSON.stringify({ rules: item.rules }, null, 2)); |
|||
} else { |
|||
const header = [['', 'pathname', 'method', 'dest'].map(s => chalk.dim(s))] |
|||
const text = list.length === 0 ? null : table(header.concat(item.rules.map(rule => { |
|||
return [ |
|||
'', |
|||
rule.pathname ? rule.pathname : '', |
|||
rule.method ? rule.method : '*', |
|||
rule.dest |
|||
] |
|||
})), {align: ['l', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen}) |
|||
|
|||
console.log(text) |
|||
const header = [ |
|||
["", "pathname", "method", "dest"].map(s => chalk.dim(s)) |
|||
]; |
|||
const text = list.length === 0 |
|||
? null |
|||
: table( |
|||
header.concat( |
|||
item.rules.map(rule => { |
|||
return [ |
|||
"", |
|||
rule.pathname ? rule.pathname : "", |
|||
rule.method ? rule.method : "*", |
|||
rule.dest |
|||
]; |
|||
}) |
|||
), |
|||
{ |
|||
align: ["l", "l", "l", "l"], |
|||
hsep: " ".repeat(2), |
|||
stringLength: strlen |
|||
} |
|||
); |
|||
|
|||
console.log(text); |
|||
} |
|||
break |
|||
break; |
|||
} else if (args.length !== 0) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias ls`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now alias ls`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
|
|||
const start_ = new Date() |
|||
const list = await alias.list() |
|||
const urls = new Map(list.map(l => [l.uid, l.url])) |
|||
const aliases = await alias.ls() |
|||
aliases.sort((a, b) => new Date(b.created) - new Date(a.created)) |
|||
const current = new Date() |
|||
|
|||
const header = [['', 'id', 'source', 'url', 'created'].map(s => chalk.dim(s))] |
|||
const text = list.length === 0 ? null : table(header.concat(aliases.map(_alias => { |
|||
const _url = chalk.underline(`https://${_alias.alias}`) |
|||
const target = _alias.deploymentId |
|||
let _sourceUrl |
|||
if (urls.get(target)) { |
|||
_sourceUrl = chalk.underline(`https://${urls.get(target)}`) |
|||
} else if (_alias.rules) { |
|||
_sourceUrl = chalk.gray(`[${_alias.rules.length} custom rule${_alias.rules.length > 1 ? 's' : ''}]`) |
|||
} else { |
|||
_sourceUrl = chalk.gray('<null>') |
|||
} |
|||
|
|||
const time = chalk.gray(ms(current - new Date(_alias.created)) + ' ago') |
|||
return [ |
|||
'', |
|||
// we default to `''` because some early aliases didn't
|
|||
// have an uid associated
|
|||
_alias.uid === null ? '' : _alias.uid, |
|||
_sourceUrl, |
|||
_url, |
|||
time |
|||
] |
|||
})), {align: ['l', 'r', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen}) |
|||
|
|||
const elapsed_ = ms(new Date() - start_) |
|||
console.log(`> ${aliases.length} alias${aliases.length === 1 ? '' : 'es'} found ${chalk.gray(`[${elapsed_}]`)}`) |
|||
const start_ = new Date(); |
|||
const list = await alias.list(); |
|||
const urls = new Map(list.map(l => [l.uid, l.url])); |
|||
const aliases = await alias.ls(); |
|||
aliases.sort((a, b) => new Date(b.created) - new Date(a.created)); |
|||
const current = new Date(); |
|||
|
|||
const header = [ |
|||
["", "id", "source", "url", "created"].map(s => chalk.dim(s)) |
|||
]; |
|||
const text = list.length === 0 |
|||
? null |
|||
: table( |
|||
header.concat( |
|||
aliases.map(_alias => { |
|||
const _url = chalk.underline(`https://${_alias.alias}`); |
|||
const target = _alias.deploymentId; |
|||
let _sourceUrl; |
|||
if (urls.get(target)) { |
|||
_sourceUrl = chalk.underline(`https://${urls.get(target)}`); |
|||
} else if (_alias.rules) { |
|||
_sourceUrl = chalk.gray( |
|||
`[${_alias.rules.length} custom rule${_alias.rules.length > 1 ? "s" : ""}]` |
|||
); |
|||
} else { |
|||
_sourceUrl = chalk.gray("<null>"); |
|||
} |
|||
|
|||
const time = chalk.gray( |
|||
ms(current - new Date(_alias.created)) + " ago" |
|||
); |
|||
return [ |
|||
"", |
|||
// we default to `''` because some early aliases didn't
|
|||
// have an uid associated
|
|||
_alias.uid === null ? "" : _alias.uid, |
|||
_sourceUrl, |
|||
_url, |
|||
time |
|||
]; |
|||
}) |
|||
), |
|||
{ |
|||
align: ["l", "r", "l", "l"], |
|||
hsep: " ".repeat(2), |
|||
stringLength: strlen |
|||
} |
|||
); |
|||
|
|||
const elapsed_ = ms(new Date() - start_); |
|||
console.log( |
|||
`> ${aliases.length} alias${aliases.length === 1 ? "" : "es"} found ${chalk.gray(`[${elapsed_}]`)}` |
|||
); |
|||
|
|||
if (text) { |
|||
console.log('\n' + text + '\n') |
|||
console.log("\n" + text + "\n"); |
|||
} |
|||
|
|||
break |
|||
break; |
|||
} |
|||
case 'rm': |
|||
case 'remove': { |
|||
const _target = String(args[0]) |
|||
case "rm": |
|||
case "remove": { |
|||
const _target = String(args[0]); |
|||
if (!_target) { |
|||
const err = new Error('No alias id specified') |
|||
err.userError = true |
|||
throw err |
|||
const err = new Error("No alias id specified"); |
|||
err.userError = true; |
|||
throw err; |
|||
} |
|||
|
|||
if (args.length !== 1) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias rm <id>`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now alias rm <id>`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
|
|||
const _aliases = await alias.ls() |
|||
const _alias = findAlias(_target, _aliases) |
|||
const _aliases = await alias.ls(); |
|||
const _alias = findAlias(_target, _aliases); |
|||
|
|||
if (!_alias) { |
|||
const err = new Error(`Alias not found by "${_target}". Run ${chalk.dim('`now alias ls`')} to see your aliases.`) |
|||
err.userError = true |
|||
throw err |
|||
const err = new Error( |
|||
`Alias not found by "${_target}". Run ${chalk.dim("`now alias ls`")} to see your aliases.` |
|||
); |
|||
err.userError = true; |
|||
throw err; |
|||
} |
|||
|
|||
try { |
|||
const confirmation = await confirmDeploymentRemoval(alias, _alias) |
|||
const confirmation = await confirmDeploymentRemoval(alias, _alias); |
|||
if (!confirmation) { |
|||
console.log('\n> Aborted') |
|||
process.exit(0) |
|||
console.log("\n> Aborted"); |
|||
process.exit(0); |
|||
} |
|||
|
|||
const start = new Date() |
|||
await alias.rm(_alias) |
|||
const elapsed = ms(new Date() - start) |
|||
console.log(`${chalk.cyan('> Success!')} Alias ${chalk.bold(_alias.uid)} removed [${elapsed}]`) |
|||
const start = new Date(); |
|||
await alias.rm(_alias); |
|||
const elapsed = ms(new Date() - start); |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Alias ${chalk.bold(_alias.uid)} removed [${elapsed}]` |
|||
); |
|||
} catch (err) { |
|||
error(err) |
|||
exit(1) |
|||
error(err); |
|||
exit(1); |
|||
} |
|||
|
|||
break |
|||
break; |
|||
} |
|||
case 'add': |
|||
case 'set': { |
|||
case "add": |
|||
case "set": { |
|||
if (argv.rules) { |
|||
await updatePathAlias(alias, argv._[0], argv.rules) |
|||
break |
|||
await updatePathAlias(alias, argv._[0], argv.rules); |
|||
break; |
|||
} |
|||
if (args.length !== 2) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias set <id> <domain>`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now alias set <id> <domain>`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
await alias.set(String(args[0]), String(args[1])) |
|||
break |
|||
await alias.set(String(args[0]), String(args[1])); |
|||
break; |
|||
} |
|||
default: { |
|||
if (argv._.length === 0) { |
|||
await reAlias(token, null, help, exit, apiUrl, debug, alias) |
|||
break |
|||
await reAlias(token, null, help, exit, apiUrl, debug, alias); |
|||
break; |
|||
} |
|||
|
|||
if (argv.rules) { |
|||
await updatePathAlias(alias, argv._[0], argv.rules) |
|||
await updatePathAlias(alias, argv._[0], argv.rules); |
|||
} else if (argv._.length === 2) { |
|||
await alias.set(String(argv._[0]), String(argv._[1])) |
|||
await alias.set(String(argv._[0]), String(argv._[1])); |
|||
} else if (argv._.length >= 3) { |
|||
error('Invalid number of arguments') |
|||
help() |
|||
exit(1) |
|||
error("Invalid number of arguments"); |
|||
help(); |
|||
exit(1); |
|||
} else { |
|||
error('Please specify a valid subcommand: ls | set | rm') |
|||
help() |
|||
exit(1) |
|||
error("Please specify a valid subcommand: ls | set | rm"); |
|||
help(); |
|||
exit(1); |
|||
} |
|||
} |
|||
} |
|||
|
|||
alias.close() |
|||
alias.close(); |
|||
} |
|||
|
|||
async function confirmDeploymentRemoval(alias, _alias) { |
|||
const deploymentsList = await alias.list() |
|||
const urls = new Map(deploymentsList.map(l => [l.uid, l.url])) |
|||
const deploymentsList = await alias.list(); |
|||
const urls = new Map(deploymentsList.map(l => [l.uid, l.url])); |
|||
|
|||
const time = chalk.gray(ms(new Date() - new Date(_alias.created)) + ' ago') |
|||
const _sourceUrl = chalk.underline(`https://${urls.get(_alias.deploymentId)}`) |
|||
const time = chalk.gray(ms(new Date() - new Date(_alias.created)) + " ago"); |
|||
const _sourceUrl = chalk.underline( |
|||
`https://${urls.get(_alias.deploymentId)}` |
|||
); |
|||
const tbl = table( |
|||
[[_alias.uid, _sourceUrl, chalk.underline(`https://${_alias.alias}`), time]], |
|||
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} |
|||
) |
|||
|
|||
const msg = '> The following alias will be removed permanently\n' + |
|||
` ${tbl} \nAre you sure?` |
|||
return await promptBool(msg) |
|||
[ |
|||
[_alias.uid, _sourceUrl, chalk.underline(`https://${_alias.alias}`), time] |
|||
], |
|||
{ align: ["l", "r", "l"], hsep: " ".repeat(6) } |
|||
); |
|||
|
|||
const msg = "> The following alias will be removed permanently\n" + |
|||
` ${tbl} \nAre you sure?`; |
|||
return await promptBool(msg); |
|||
} |
|||
|
|||
function findAlias(alias, list) { |
|||
let key |
|||
let val |
|||
let key; |
|||
let val; |
|||
|
|||
if (/\./.test(alias)) { |
|||
val = toHost(alias) |
|||
key = 'alias' |
|||
val = toHost(alias); |
|||
key = "alias"; |
|||
} else { |
|||
val = alias |
|||
key = 'uid' |
|||
val = alias; |
|||
key = "uid"; |
|||
} |
|||
|
|||
const _alias = list.find(d => { |
|||
if (d[key] === val) { |
|||
if (debug) { |
|||
console.log(`> [debug] matched alias ${d.uid} by ${key} ${val}`) |
|||
console.log(`> [debug] matched alias ${d.uid} by ${key} ${val}`); |
|||
} |
|||
|
|||
return true |
|||
return true; |
|||
} |
|||
|
|||
// match prefix
|
|||
if (`${val}.now.sh` === d.alias) { |
|||
if (debug) { |
|||
console.log(`> [debug] matched alias ${d.uid} by url ${d.host}`) |
|||
console.log(`> [debug] matched alias ${d.uid} by url ${d.host}`); |
|||
} |
|||
|
|||
return true |
|||
return true; |
|||
} |
|||
|
|||
return false |
|||
}) |
|||
return false; |
|||
}); |
|||
|
|||
return _alias |
|||
return _alias; |
|||
} |
|||
|
|||
async function updatePathAlias(alias, aliasName, rules) { |
|||
const start = new Date() |
|||
const res = await alias.updatePathBasedroutes(String(aliasName), rules) |
|||
const elapsed = ms(new Date() - start) |
|||
const start = new Date(); |
|||
const res = await alias.updatePathBasedroutes(String(aliasName), rules); |
|||
const elapsed = ms(new Date() - start); |
|||
if (res.error) { |
|||
const err = new Error(res.error.message) |
|||
err.userError = true |
|||
throw err |
|||
const err = new Error(res.error.message); |
|||
err.userError = true; |
|||
throw err; |
|||
} else { |
|||
console.log(`${chalk.cyan('> Success!')} ${res.ruleCount} rules configured for ${chalk.underline(res.alias)} [${elapsed}]`) |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} ${res.ruleCount} rules configured for ${chalk.underline(res.alias)} [${elapsed}]` |
|||
); |
|||
} |
|||
} |
|||
|
@ -1,287 +1,342 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
// Native
|
|||
const path = require('path') |
|||
const path = require("path"); |
|||
|
|||
// Packages
|
|||
const chalk = require('chalk') |
|||
const table = require('text-table') |
|||
const minimist = require('minimist') |
|||
const fs = require('fs-promise') |
|||
const ms = require('ms') |
|||
const chalk = require("chalk"); |
|||
const table = require("text-table"); |
|||
const minimist = require("minimist"); |
|||
const fs = require("fs-promise"); |
|||
const ms = require("ms"); |
|||
|
|||
// Ours
|
|||
const strlen = require('../lib/strlen') |
|||
const cfg = require('../lib/cfg') |
|||
const {handleError, error} = require('../lib/error') |
|||
const NowCerts = require('../lib/certs') |
|||
const login = require('../lib/login') |
|||
const exit = require('../lib/utils/exit') |
|||
const logo = require('../lib/utils/output/logo') |
|||
const strlen = require("../lib/strlen"); |
|||
const cfg = require("../lib/cfg"); |
|||
const { handleError, error } = require("../lib/error"); |
|||
const NowCerts = require("../lib/certs"); |
|||
const login = require("../lib/login"); |
|||
const exit = require("../lib/utils/exit"); |
|||
const logo = require("../lib/utils/output/logo"); |
|||
|
|||
const argv = minimist(process.argv.slice(2), { |
|||
string: ['config', 'token', 'crt', 'key', 'ca'], |
|||
boolean: ['help', 'debug'], |
|||
string: ["config", "token", "crt", "key", "ca"], |
|||
boolean: ["help", "debug"], |
|||
alias: { |
|||
help: 'h', |
|||
config: 'c', |
|||
debug: 'd', |
|||
token: 't' |
|||
help: "h", |
|||
config: "c", |
|||
debug: "d", |
|||
token: "t" |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
const subcommand = argv._[0] |
|||
const subcommand = argv._[0]; |
|||
|
|||
// options
|
|||
const help = () => { |
|||
console.log(` |
|||
console.log( |
|||
` |
|||
${chalk.bold(`${logo} now certs`)} <ls | create | renew | replace | rm> <cn> |
|||
|
|||
${chalk.dim('Note:')} |
|||
${chalk.dim("Note:")} |
|||
|
|||
This command is intended for advanced use only, normally ${chalk.bold('now')} manages your certificates automatically. |
|||
This command is intended for advanced use only, normally ${chalk.bold("now")} manages your certificates automatically. |
|||
|
|||
${chalk.dim('Options:')} |
|||
${chalk.dim("Options:")} |
|||
|
|||
-h, --help Output usage information |
|||
-c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file |
|||
-c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file |
|||
-d, --debug Debug mode [off] |
|||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token |
|||
--crt ${chalk.bold.underline('FILE')} Certificate file |
|||
--key ${chalk.bold.underline('FILE')} Certificate key file |
|||
--ca ${chalk.bold.underline('FILE')} CA certificate chain file |
|||
-t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token |
|||
--crt ${chalk.bold.underline("FILE")} Certificate file |
|||
--key ${chalk.bold.underline("FILE")} Certificate key file |
|||
--ca ${chalk.bold.underline("FILE")} CA certificate chain file |
|||
|
|||
${chalk.dim('Examples:')} |
|||
${chalk.dim("Examples:")} |
|||
|
|||
${chalk.gray('–')} Listing all your certificates: |
|||
${chalk.gray("–")} Listing all your certificates: |
|||
|
|||
${chalk.cyan('$ now certs ls')} |
|||
${chalk.cyan("$ now certs ls")} |
|||
|
|||
${chalk.gray('–')} Creating a new certificate: |
|||
${chalk.gray("–")} Creating a new certificate: |
|||
|
|||
${chalk.cyan('$ now certs create domain.com')} |
|||
${chalk.cyan("$ now certs create domain.com")} |
|||
|
|||
${chalk.gray('–')} Renewing an existing certificate issued with ${chalk.bold('now')}: |
|||
${chalk.gray("–")} Renewing an existing certificate issued with ${chalk.bold("now")}: |
|||
|
|||
${chalk.cyan('$ now certs renew domain.com')} |
|||
${chalk.cyan("$ now certs renew domain.com")} |
|||
|
|||
${chalk.gray('–')} Replacing an existing certificate with a user-supplied certificate: |
|||
${chalk.gray("–")} Replacing an existing certificate with a user-supplied certificate: |
|||
|
|||
${chalk.cyan('$ now certs replace --crt domain.crt --key domain.key --ca ca_chain.crt domain.com')} |
|||
`)
|
|||
} |
|||
${chalk.cyan("$ now certs replace --crt domain.crt --key domain.key --ca ca_chain.crt domain.com")} |
|||
` |
|||
); |
|||
}; |
|||
|
|||
// options
|
|||
const debug = argv.debug |
|||
const apiUrl = argv.url || 'https://api.zeit.co' |
|||
const debug = argv.debug; |
|||
const apiUrl = argv.url || "https://api.zeit.co"; |
|||
|
|||
if (argv.config) { |
|||
cfg.setConfigFile(argv.config) |
|||
cfg.setConfigFile(argv.config); |
|||
} |
|||
|
|||
if (argv.help || !subcommand) { |
|||
help() |
|||
exit(0) |
|||
help(); |
|||
exit(0); |
|||
} else { |
|||
const config = cfg.read() |
|||
const config = cfg.read(); |
|||
|
|||
Promise.resolve(argv.token || config.token || login(apiUrl)) |
|||
.then(async token => { |
|||
try { |
|||
await run(token) |
|||
} catch (err) { |
|||
handleError(err) |
|||
exit(1) |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`) |
|||
exit(1) |
|||
}) |
|||
.then(async token => { |
|||
try { |
|||
await run(token); |
|||
} catch (err) { |
|||
handleError(err); |
|||
exit(1); |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`); |
|||
exit(1); |
|||
}); |
|||
} |
|||
|
|||
function formatExpirationDate(date) { |
|||
const diff = date - Date.now() |
|||
return diff < 0 ? chalk.gray(ms(-diff) + ' ago') : chalk.gray('in ' + ms(diff)) |
|||
const diff = date - Date.now(); |
|||
return diff < 0 |
|||
? chalk.gray(ms(-diff) + " ago") |
|||
: chalk.gray("in " + ms(diff)); |
|||
} |
|||
|
|||
async function run(token) { |
|||
const certs = new NowCerts(apiUrl, token, {debug}) |
|||
const args = argv._.slice(1) |
|||
const start = Date.now() |
|||
const certs = new NowCerts(apiUrl, token, { debug }); |
|||
const args = argv._.slice(1); |
|||
const start = Date.now(); |
|||
|
|||
if (subcommand === 'ls' || subcommand === 'list') { |
|||
if (subcommand === "ls" || subcommand === "list") { |
|||
if (args.length !== 0) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs ls`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now certs ls`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
|
|||
const list = await certs.ls() |
|||
const elapsed = ms(new Date() - start) |
|||
const list = await certs.ls(); |
|||
const elapsed = ms(new Date() - start); |
|||
|
|||
console.log(`> ${list.length} certificate${list.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`) |
|||
console.log( |
|||
`> ${list.length} certificate${list.length === 1 ? "" : "s"} found ${chalk.gray(`[${elapsed}]`)}` |
|||
); |
|||
|
|||
if (list.length > 0) { |
|||
const cur = Date.now() |
|||
const cur = Date.now(); |
|||
list.sort((a, b) => { |
|||
return a.cn.localeCompare(b.cn) |
|||
}) |
|||
const header = [['', 'id', 'cn', 'created', 'expiration', 'auto-renew'].map(s => chalk.dim(s))] |
|||
const out = table(header.concat(list.map(cert => { |
|||
const cn = chalk.bold(cert.cn) |
|||
const time = chalk.gray(ms(cur - new Date(cert.created)) + ' ago') |
|||
const expiration = formatExpirationDate(new Date(cert.expiration)) |
|||
return [ |
|||
'', |
|||
cert.uid ? cert.uid : 'unknown', |
|||
cn, |
|||
time, |
|||
expiration, |
|||
cert.autoRenew ? 'yes' : 'no' |
|||
] |
|||
})), {align: ['l', 'r', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen}) |
|||
return a.cn.localeCompare(b.cn); |
|||
}); |
|||
const header = [ |
|||
["", "id", "cn", "created", "expiration", "auto-renew"].map(s => |
|||
chalk.dim(s)) |
|||
]; |
|||
const out = table( |
|||
header.concat( |
|||
list.map(cert => { |
|||
const cn = chalk.bold(cert.cn); |
|||
const time = chalk.gray(ms(cur - new Date(cert.created)) + " ago"); |
|||
const expiration = formatExpirationDate(new Date(cert.expiration)); |
|||
return [ |
|||
"", |
|||
cert.uid ? cert.uid : "unknown", |
|||
cn, |
|||
time, |
|||
expiration, |
|||
cert.autoRenew ? "yes" : "no" |
|||
]; |
|||
}) |
|||
), |
|||
{ |
|||
align: ["l", "r", "l", "l", "l"], |
|||
hsep: " ".repeat(2), |
|||
stringLength: strlen |
|||
} |
|||
); |
|||
|
|||
if (out) { |
|||
console.log('\n' + out + '\n') |
|||
console.log("\n" + out + "\n"); |
|||
} |
|||
} |
|||
} else if (subcommand === 'create') { |
|||
} else if (subcommand === "create") { |
|||
if (args.length !== 1) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs create <cn>`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now certs create <cn>`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
const cn = args[0] |
|||
let cert |
|||
const cn = args[0]; |
|||
let cert; |
|||
|
|||
if (argv.crt || argv.key || argv.ca) { // Issue a custom certificate
|
|||
if (argv.crt || argv.key || argv.ca) { |
|||
// Issue a custom certificate
|
|||
if (!argv.crt || !argv.key) { |
|||
error(`Missing required arguments for a custom certificate entry. Usage: ${chalk.cyan('`now certs create --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Missing required arguments for a custom certificate entry. Usage: ${chalk.cyan("`now certs create --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
|
|||
const crt = readX509File(argv.crt) |
|||
const key = readX509File(argv.key) |
|||
const ca = argv.ca ? readX509File(argv.ca) : '' |
|||
const crt = readX509File(argv.crt); |
|||
const key = readX509File(argv.key); |
|||
const ca = argv.ca ? readX509File(argv.ca) : ""; |
|||
|
|||
cert = await certs.put(cn, crt, key, ca) |
|||
} else { // Issue a standard certificate
|
|||
cert = await certs.create(cn) |
|||
cert = await certs.put(cn, crt, key, ca); |
|||
} else { |
|||
// Issue a standard certificate
|
|||
cert = await certs.create(cn); |
|||
} |
|||
if (!cert) { |
|||
// Cert is undefined and "Cert is already issued" has been printed to stdout
|
|||
return exit(1) |
|||
return exit(1); |
|||
} |
|||
const elapsed = ms(new Date() - start) |
|||
console.log(`${chalk.cyan('> Success!')} Certificate entry ${chalk.bold(cn)} ${chalk.gray(`(${cert.uid})`)} created ${chalk.gray(`[${elapsed}]`)}`) |
|||
} else if (subcommand === 'renew') { |
|||
const elapsed = ms(new Date() - start); |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Certificate entry ${chalk.bold(cn)} ${chalk.gray(`(${cert.uid})`)} created ${chalk.gray(`[${elapsed}]`)}` |
|||
); |
|||
} else if (subcommand === "renew") { |
|||
if (args.length !== 1) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs renew <id | cn>`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now certs renew <id | cn>`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
|
|||
const cert = await getCertIdCn(certs, args[0]) |
|||
const cert = await getCertIdCn(certs, args[0]); |
|||
if (!cert) { |
|||
return exit(1) |
|||
return exit(1); |
|||
} |
|||
const yes = await readConfirmation(cert, 'The following certificate will be renewed\n') |
|||
const yes = await readConfirmation( |
|||
cert, |
|||
"The following certificate will be renewed\n" |
|||
); |
|||
|
|||
if (!yes) { |
|||
error('User abort') |
|||
return exit(0) |
|||
error("User abort"); |
|||
return exit(0); |
|||
} |
|||
|
|||
await certs.renew(cert.cn) |
|||
const elapsed = ms(new Date() - start) |
|||
console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} renewed ${chalk.gray(`[${elapsed}]`)}`) |
|||
} else if (subcommand === 'replace') { |
|||
await certs.renew(cert.cn); |
|||
const elapsed = ms(new Date() - start); |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} renewed ${chalk.gray(`[${elapsed}]`)}` |
|||
); |
|||
} else if (subcommand === "replace") { |
|||
if (!argv.crt || !argv.key) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs replace --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now certs replace --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
|
|||
const crt = readX509File(argv.crt) |
|||
const key = readX509File(argv.key) |
|||
const ca = argv.ca ? readX509File(argv.ca) : '' |
|||
const crt = readX509File(argv.crt); |
|||
const key = readX509File(argv.key); |
|||
const ca = argv.ca ? readX509File(argv.ca) : ""; |
|||
|
|||
const cert = await getCertIdCn(certs, args[0]) |
|||
const cert = await getCertIdCn(certs, args[0]); |
|||
if (!cert) { |
|||
return exit(1) |
|||
return exit(1); |
|||
} |
|||
const yes = await readConfirmation(cert, 'The following certificate will be replaced permanently\n') |
|||
const yes = await readConfirmation( |
|||
cert, |
|||
"The following certificate will be replaced permanently\n" |
|||
); |
|||
if (!yes) { |
|||
error('User abort') |
|||
return exit(0) |
|||
error("User abort"); |
|||
return exit(0); |
|||
} |
|||
|
|||
await certs.put(cert.cn, crt, key, ca) |
|||
const elapsed = ms(new Date() - start) |
|||
console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} replaced ${chalk.gray(`[${elapsed}]`)}`) |
|||
} else if (subcommand === 'rm' || subcommand === 'remove') { |
|||
await certs.put(cert.cn, crt, key, ca); |
|||
const elapsed = ms(new Date() - start); |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} replaced ${chalk.gray(`[${elapsed}]`)}` |
|||
); |
|||
} else if (subcommand === "rm" || subcommand === "remove") { |
|||
if (args.length !== 1) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs rm <id | cn>`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now certs rm <id | cn>`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
|
|||
const cert = await getCertIdCn(certs, args[0]) |
|||
const cert = await getCertIdCn(certs, args[0]); |
|||
if (!cert) { |
|||
return exit(1) |
|||
return exit(1); |
|||
} |
|||
const yes = await readConfirmation(cert, 'The following certificate will be removed permanently\n') |
|||
const yes = await readConfirmation( |
|||
cert, |
|||
"The following certificate will be removed permanently\n" |
|||
); |
|||
if (!yes) { |
|||
error('User abort') |
|||
return exit(0) |
|||
error("User abort"); |
|||
return exit(0); |
|||
} |
|||
|
|||
await certs.delete(cert.cn) |
|||
const elapsed = ms(new Date() - start) |
|||
console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}`) |
|||
await certs.delete(cert.cn); |
|||
const elapsed = ms(new Date() - start); |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}` |
|||
); |
|||
} else { |
|||
error('Please specify a valid subcommand: ls | create | renew | replace | rm') |
|||
help() |
|||
exit(1) |
|||
error( |
|||
"Please specify a valid subcommand: ls | create | renew | replace | rm" |
|||
); |
|||
help(); |
|||
exit(1); |
|||
} |
|||
return certs.close() |
|||
return certs.close(); |
|||
} |
|||
|
|||
process.on('uncaughtException', err => { |
|||
handleError(err) |
|||
exit(1) |
|||
}) |
|||
process.on("uncaughtException", err => { |
|||
handleError(err); |
|||
exit(1); |
|||
}); |
|||
|
|||
function readConfirmation(cert, msg) { |
|||
return new Promise(resolve => { |
|||
const time = chalk.gray(ms(new Date() - new Date(cert.created)) + ' ago') |
|||
const tbl = table( |
|||
[[cert.uid, chalk.bold(cert.cn), time]], |
|||
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} |
|||
) |
|||
|
|||
process.stdout.write(`> ${msg}`) |
|||
process.stdout.write(' ' + tbl + '\n') |
|||
|
|||
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[y/N] ')}`) |
|||
|
|||
process.stdin.on('data', d => { |
|||
process.stdin.pause() |
|||
resolve(d.toString().trim().toLowerCase() === 'y') |
|||
}).resume() |
|||
}) |
|||
const time = chalk.gray(ms(new Date() - new Date(cert.created)) + " ago"); |
|||
const tbl = table([[cert.uid, chalk.bold(cert.cn), time]], { |
|||
align: ["l", "r", "l"], |
|||
hsep: " ".repeat(6) |
|||
}); |
|||
|
|||
process.stdout.write(`> ${msg}`); |
|||
process.stdout.write(" " + tbl + "\n"); |
|||
|
|||
process.stdout.write( |
|||
`${chalk.bold.red("> Are you sure?")} ${chalk.gray("[y/N] ")}` |
|||
); |
|||
|
|||
process.stdin |
|||
.on("data", d => { |
|||
process.stdin.pause(); |
|||
resolve(d.toString().trim().toLowerCase() === "y"); |
|||
}) |
|||
.resume(); |
|||
}); |
|||
} |
|||
|
|||
function readX509File(file) { |
|||
return fs.readFileSync(path.resolve(file), 'utf8') |
|||
return fs.readFileSync(path.resolve(file), "utf8"); |
|||
} |
|||
|
|||
async function getCertIdCn(certs, idOrCn) { |
|||
const list = await certs.ls() |
|||
const list = await certs.ls(); |
|||
const thecert = list.filter(cert => { |
|||
return cert.uid === idOrCn || cert.cn === idOrCn |
|||
})[0] |
|||
return cert.uid === idOrCn || cert.cn === idOrCn; |
|||
})[0]; |
|||
|
|||
if (!thecert) { |
|||
error(`No certificate found by id or cn "${idOrCn}"`) |
|||
return null |
|||
error(`No certificate found by id or cn "${idOrCn}"`); |
|||
return null; |
|||
} |
|||
|
|||
return thecert |
|||
return thecert; |
|||
} |
|||
|
@ -1,291 +1,333 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
// Packages
|
|||
const chalk = require('chalk') |
|||
const minimist = require('minimist') |
|||
const table = require('text-table') |
|||
const ms = require('ms') |
|||
const chalk = require("chalk"); |
|||
const minimist = require("minimist"); |
|||
const table = require("text-table"); |
|||
const ms = require("ms"); |
|||
|
|||
// Ours
|
|||
const login = require('../lib/login') |
|||
const cfg = require('../lib/cfg') |
|||
const {error} = require('../lib/error') |
|||
const toHost = require('../lib/to-host') |
|||
const strlen = require('../lib/strlen') |
|||
const NowDomains = require('../lib/domains') |
|||
const exit = require('../lib/utils/exit') |
|||
const logo = require('../lib/utils/output/logo') |
|||
const login = require("../lib/login"); |
|||
const cfg = require("../lib/cfg"); |
|||
const { error } = require("../lib/error"); |
|||
const toHost = require("../lib/to-host"); |
|||
const strlen = require("../lib/strlen"); |
|||
const NowDomains = require("../lib/domains"); |
|||
const exit = require("../lib/utils/exit"); |
|||
const logo = require("../lib/utils/output/logo"); |
|||
|
|||
const argv = minimist(process.argv.slice(2), { |
|||
string: ['config', 'token'], |
|||
boolean: ['help', 'debug', 'external', 'force'], |
|||
string: ["config", "token"], |
|||
boolean: ["help", "debug", "external", "force"], |
|||
alias: { |
|||
help: 'h', |
|||
config: 'c', |
|||
debug: 'd', |
|||
external: 'e', |
|||
force: 'f', |
|||
token: 't' |
|||
help: "h", |
|||
config: "c", |
|||
debug: "d", |
|||
external: "e", |
|||
force: "f", |
|||
token: "t" |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
const subcommand = argv._[0] |
|||
const subcommand = argv._[0]; |
|||
|
|||
// options
|
|||
const help = () => { |
|||
console.log(` |
|||
console.log( |
|||
` |
|||
${chalk.bold(`${logo} now domains`)} <ls | add | rm> <domain> |
|||
|
|||
${chalk.dim('Options:')} |
|||
${chalk.dim("Options:")} |
|||
|
|||
-h, --help Output usage information |
|||
-c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file |
|||
-c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file |
|||
-d, --debug Debug mode [off] |
|||
-e, --external Use external DNS server |
|||
-f, --force Skip DNS verification |
|||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token |
|||
-t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token |
|||
|
|||
${chalk.dim('Examples:')} |
|||
${chalk.dim("Examples:")} |
|||
|
|||
${chalk.gray('–')} Lists all your domains: |
|||
${chalk.gray("–")} Lists all your domains: |
|||
|
|||
${chalk.cyan('$ now domains ls')} |
|||
${chalk.cyan("$ now domains ls")} |
|||
|
|||
${chalk.gray('–')} Adds a domain name: |
|||
${chalk.gray("–")} Adds a domain name: |
|||
|
|||
${chalk.cyan(`$ now domains add ${chalk.underline('my-app.com')}`)} |
|||
${chalk.cyan(`$ now domains add ${chalk.underline("my-app.com")}`)} |
|||
|
|||
Make sure the domain's DNS nameservers are at least 2 of these: |
|||
|
|||
${chalk.gray('–')} ${chalk.underline('california.zeit.world')} ${chalk.dim('173.255.215.107')} |
|||
${chalk.gray('–')} ${chalk.underline('london.zeit.world')} ${chalk.dim('178.62.47.76')} |
|||
${chalk.gray('–')} ${chalk.underline('newark.zeit.world')} ${chalk.dim('173.255.231.87')} |
|||
${chalk.gray('–')} ${chalk.underline('amsterdam.zeit.world')} ${chalk.dim('188.226.197.55')} |
|||
${chalk.gray('–')} ${chalk.underline('dallas.zeit.world')} ${chalk.dim('173.192.101.194')} |
|||
${chalk.gray('–')} ${chalk.underline('paris.zeit.world')} ${chalk.dim('37.123.115.172')} |
|||
${chalk.gray('–')} ${chalk.underline('singapore.zeit.world')} ${chalk.dim('119.81.97.170')} |
|||
${chalk.gray('–')} ${chalk.underline('sydney.zeit.world')} ${chalk.dim('52.64.171.200')} |
|||
${chalk.gray('–')} ${chalk.underline('frankfurt.zeit.world')} ${chalk.dim('91.109.245.139')} |
|||
${chalk.gray('–')} ${chalk.underline('iowa.zeit.world')} ${chalk.dim('23.236.59.22')} |
|||
${chalk.gray("–")} ${chalk.underline("california.zeit.world")} ${chalk.dim("173.255.215.107")} |
|||
${chalk.gray("–")} ${chalk.underline("london.zeit.world")} ${chalk.dim("178.62.47.76")} |
|||
${chalk.gray("–")} ${chalk.underline("newark.zeit.world")} ${chalk.dim("173.255.231.87")} |
|||
${chalk.gray("–")} ${chalk.underline("amsterdam.zeit.world")} ${chalk.dim("188.226.197.55")} |
|||
${chalk.gray("–")} ${chalk.underline("dallas.zeit.world")} ${chalk.dim("173.192.101.194")} |
|||
${chalk.gray("–")} ${chalk.underline("paris.zeit.world")} ${chalk.dim("37.123.115.172")} |
|||
${chalk.gray("–")} ${chalk.underline("singapore.zeit.world")} ${chalk.dim("119.81.97.170")} |
|||
${chalk.gray("–")} ${chalk.underline("sydney.zeit.world")} ${chalk.dim("52.64.171.200")} |
|||
${chalk.gray("–")} ${chalk.underline("frankfurt.zeit.world")} ${chalk.dim("91.109.245.139")} |
|||
${chalk.gray("–")} ${chalk.underline("iowa.zeit.world")} ${chalk.dim("23.236.59.22")} |
|||
|
|||
${chalk.yellow('NOTE:')} running ${chalk.dim('`now alias`')} will automatically register your domain |
|||
if it's configured with these nameservers (no need to ${chalk.dim('`domain add`')}). |
|||
${chalk.yellow("NOTE:")} running ${chalk.dim("`now alias`")} will automatically register your domain |
|||
if it's configured with these nameservers (no need to ${chalk.dim("`domain add`")}). |
|||
|
|||
For more details head to ${chalk.underline('https://zeit.world')}. |
|||
For more details head to ${chalk.underline("https://zeit.world")}. |
|||
|
|||
${chalk.gray('–')} Removing a domain: |
|||
${chalk.gray("–")} Removing a domain: |
|||
|
|||
${chalk.cyan('$ now domain rm my-app.com')} |
|||
${chalk.cyan("$ now domain rm my-app.com")} |
|||
|
|||
or |
|||
|
|||
${chalk.cyan('$ now domain rm domainId')} |
|||
${chalk.cyan("$ now domain rm domainId")} |
|||
|
|||
To get the list of domain ids, use ${chalk.dim('`now domains ls`')}. |
|||
To get the list of domain ids, use ${chalk.dim("`now domains ls`")}. |
|||
|
|||
${chalk.gray('–')} Adding and verifying a domain name using zeit.world nameservers: |
|||
${chalk.gray("–")} Adding and verifying a domain name using zeit.world nameservers: |
|||
|
|||
${chalk.cyan('$ now domain add my-app.com')} |
|||
${chalk.cyan("$ now domain add my-app.com")} |
|||
|
|||
The command will tell you if the domain was verified succesfully. In case the domain was not verified succesfully you should retry adding the domain after some time. |
|||
|
|||
${chalk.gray('–')} Adding and verifying a domain name using an external nameserver: |
|||
${chalk.gray("–")} Adding and verifying a domain name using an external nameserver: |
|||
|
|||
${chalk.cyan('$ now domain add -e my-app.com')} |
|||
${chalk.cyan("$ now domain add -e my-app.com")} |
|||
|
|||
and follow the verification instructions if requested. Finally, rerun the same command after completing the verification step. |
|||
`)
|
|||
} |
|||
` |
|||
); |
|||
}; |
|||
|
|||
// options
|
|||
const debug = argv.debug |
|||
const apiUrl = argv.url || 'https://api.zeit.co' |
|||
const debug = argv.debug; |
|||
const apiUrl = argv.url || "https://api.zeit.co"; |
|||
|
|||
if (argv.config) { |
|||
cfg.setConfigFile(argv.config) |
|||
cfg.setConfigFile(argv.config); |
|||
} |
|||
|
|||
if (argv.help || !subcommand) { |
|||
help() |
|||
exit(0) |
|||
help(); |
|||
exit(0); |
|||
} else { |
|||
const config = cfg.read() |
|||
const config = cfg.read(); |
|||
|
|||
Promise.resolve(argv.token || config.token || login(apiUrl)) |
|||
.then(async token => { |
|||
try { |
|||
await run(token) |
|||
} catch (err) { |
|||
if (err.userError) { |
|||
error(err.message) |
|||
} else { |
|||
error(`Unknown error: ${err}\n${err.stack}`) |
|||
.then(async token => { |
|||
try { |
|||
await run(token); |
|||
} catch (err) { |
|||
if (err.userError) { |
|||
error(err.message); |
|||
} else { |
|||
error(`Unknown error: ${err}\n${err.stack}`); |
|||
} |
|||
exit(1); |
|||
} |
|||
exit(1) |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`) |
|||
exit(1) |
|||
}) |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`); |
|||
exit(1); |
|||
}); |
|||
} |
|||
|
|||
async function run(token) { |
|||
const domain = new NowDomains(apiUrl, token, {debug}) |
|||
const args = argv._.slice(1) |
|||
const domain = new NowDomains(apiUrl, token, { debug }); |
|||
const args = argv._.slice(1); |
|||
|
|||
switch (subcommand) { |
|||
case 'ls': |
|||
case 'list': { |
|||
case "ls": |
|||
case "list": { |
|||
if (args.length !== 0) { |
|||
error('Invalid number of arguments') |
|||
return exit(1) |
|||
error("Invalid number of arguments"); |
|||
return exit(1); |
|||
} |
|||
|
|||
const start_ = new Date() |
|||
const domains = await domain.ls() |
|||
domains.sort((a, b) => new Date(b.created) - new Date(a.created)) |
|||
const current = new Date() |
|||
const header = [['', 'id', 'dns', 'url', 'verified', 'created'].map(s => chalk.dim(s))] |
|||
const out = domains.length === 0 ? null : table(header.concat(domains.map(domain => { |
|||
const ns = domain.isExternal ? 'external' : 'zeit.world' |
|||
const url = chalk.underline(`https://${domain.name}`) |
|||
const time = chalk.gray(ms(current - new Date(domain.created)) + ' ago') |
|||
return [ |
|||
'', |
|||
domain.uid, |
|||
ns, |
|||
url, |
|||
domain.verified, |
|||
time |
|||
] |
|||
})), {align: ['l', 'r', 'l', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen}) |
|||
|
|||
const elapsed_ = ms(new Date() - start_) |
|||
console.log(`> ${domains.length} domain${domains.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed_}]`)}`) |
|||
const start_ = new Date(); |
|||
const domains = await domain.ls(); |
|||
domains.sort((a, b) => new Date(b.created) - new Date(a.created)); |
|||
const current = new Date(); |
|||
const header = [ |
|||
["", "id", "dns", "url", "verified", "created"].map(s => chalk.dim(s)) |
|||
]; |
|||
const out = domains.length === 0 |
|||
? null |
|||
: table( |
|||
header.concat( |
|||
domains.map(domain => { |
|||
const ns = domain.isExternal ? "external" : "zeit.world"; |
|||
const url = chalk.underline(`https://${domain.name}`); |
|||
const time = chalk.gray( |
|||
ms(current - new Date(domain.created)) + " ago" |
|||
); |
|||
return ["", domain.uid, ns, url, domain.verified, time]; |
|||
}) |
|||
), |
|||
{ |
|||
align: ["l", "r", "l", "l", "l", "l"], |
|||
hsep: " ".repeat(2), |
|||
stringLength: strlen |
|||
} |
|||
); |
|||
|
|||
const elapsed_ = ms(new Date() - start_); |
|||
console.log( |
|||
`> ${domains.length} domain${domains.length === 1 ? "" : "s"} found ${chalk.gray(`[${elapsed_}]`)}` |
|||
); |
|||
|
|||
if (out) { |
|||
console.log('\n' + out + '\n') |
|||
console.log("\n" + out + "\n"); |
|||
} |
|||
|
|||
break |
|||
break; |
|||
} |
|||
case 'rm': |
|||
case 'remove': { |
|||
case "rm": |
|||
case "remove": { |
|||
if (args.length !== 1) { |
|||
error('Invalid number of arguments') |
|||
return exit(1) |
|||
error("Invalid number of arguments"); |
|||
return exit(1); |
|||
} |
|||
|
|||
const _target = String(args[0]) |
|||
const _target = String(args[0]); |
|||
if (!_target) { |
|||
const err = new Error('No domain specified') |
|||
err.userError = true |
|||
throw err |
|||
const err = new Error("No domain specified"); |
|||
err.userError = true; |
|||
throw err; |
|||
} |
|||
|
|||
const _domains = await domain.ls() |
|||
const _domain = findDomain(_target, _domains) |
|||
const _domains = await domain.ls(); |
|||
const _domain = findDomain(_target, _domains); |
|||
|
|||
if (!_domain) { |
|||
const err = new Error(`Domain not found by "${_target}". Run ${chalk.dim('`now domains ls`')} to see your domains.`) |
|||
err.userError = true |
|||
throw err |
|||
const err = new Error( |
|||
`Domain not found by "${_target}". Run ${chalk.dim("`now domains ls`")} to see your domains.` |
|||
); |
|||
err.userError = true; |
|||
throw err; |
|||
} |
|||
|
|||
try { |
|||
const confirmation = (await readConfirmation(domain, _domain, _domains)).toLowerCase() |
|||
if (confirmation !== 'y' && confirmation !== 'yes') { |
|||
console.log('\n> Aborted') |
|||
process.exit(0) |
|||
const confirmation = (await readConfirmation( |
|||
domain, |
|||
_domain, |
|||
_domains |
|||
)).toLowerCase(); |
|||
if (confirmation !== "y" && confirmation !== "yes") { |
|||
console.log("\n> Aborted"); |
|||
process.exit(0); |
|||
} |
|||
|
|||
const start = new Date() |
|||
await domain.rm(_domain.name) |
|||
const elapsed = ms(new Date() - start) |
|||
console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(_domain.uid)} removed [${elapsed}]`) |
|||
const start = new Date(); |
|||
await domain.rm(_domain.name); |
|||
const elapsed = ms(new Date() - start); |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Domain ${chalk.bold(_domain.uid)} removed [${elapsed}]` |
|||
); |
|||
} catch (err) { |
|||
error(err) |
|||
exit(1) |
|||
error(err); |
|||
exit(1); |
|||
} |
|||
break |
|||
break; |
|||
} |
|||
case 'add': |
|||
case 'set': { |
|||
case "add": |
|||
case "set": { |
|||
if (args.length !== 1) { |
|||
error('Invalid number of arguments') |
|||
return exit(1) |
|||
error("Invalid number of arguments"); |
|||
return exit(1); |
|||
} |
|||
|
|||
const start = new Date() |
|||
const name = String(args[0]) |
|||
const {uid, code, created, verified} = await domain.add(name, argv.force, argv.external) |
|||
const elapsed = ms(new Date() - start) |
|||
const start = new Date(); |
|||
const name = String(args[0]); |
|||
const { uid, code, created, verified } = await domain.add( |
|||
name, |
|||
argv.force, |
|||
argv.external |
|||
); |
|||
const elapsed = ms(new Date() - start); |
|||
if (created) { |
|||
console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} added [${elapsed}]`) |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} added [${elapsed}]` |
|||
); |
|||
} else if (verified) { |
|||
console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} verified [${elapsed}]`) |
|||
} else if (code === 'not_modified') { |
|||
console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} already exists [${elapsed}]`) |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} verified [${elapsed}]` |
|||
); |
|||
} else if (code === "not_modified") { |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} already exists [${elapsed}]` |
|||
); |
|||
} else { |
|||
console.log('> Verification required: Please rerun this command after some time') |
|||
console.log( |
|||
"> Verification required: Please rerun this command after some time" |
|||
); |
|||
} |
|||
break |
|||
break; |
|||
} |
|||
default: |
|||
error('Please specify a valid subcommand: ls | add | rm') |
|||
help() |
|||
exit(1) |
|||
error("Please specify a valid subcommand: ls | add | rm"); |
|||
help(); |
|||
exit(1); |
|||
} |
|||
|
|||
domain.close() |
|||
domain.close(); |
|||
} |
|||
|
|||
async function readConfirmation(domain, _domain) { |
|||
return new Promise(resolve => { |
|||
const time = chalk.gray(ms(new Date() - new Date(_domain.created)) + ' ago') |
|||
const time = chalk.gray( |
|||
ms(new Date() - new Date(_domain.created)) + " ago" |
|||
); |
|||
const tbl = table( |
|||
[[_domain.uid, chalk.underline(`https://${_domain.name}`), time]], |
|||
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} |
|||
) |
|||
{ align: ["l", "r", "l"], hsep: " ".repeat(6) } |
|||
); |
|||
|
|||
process.stdout.write('> The following domain will be removed permanently\n') |
|||
process.stdout.write(' ' + tbl + '\n') |
|||
process.stdout.write( |
|||
"> The following domain will be removed permanently\n" |
|||
); |
|||
process.stdout.write(" " + tbl + "\n"); |
|||
|
|||
if (_domain.aliases.length > 0) { |
|||
process.stdout.write(`> ${chalk.yellow('Warning!')} This domain's ` + |
|||
`${chalk.bold(_domain.aliases.length + ' alias' + (_domain.aliases.length === 1 ? '' : 'es'))} ` + |
|||
`will be removed. Run ${chalk.dim('`now alias ls`')} to list.\n`) |
|||
process.stdout.write( |
|||
`> ${chalk.yellow("Warning!")} This domain's ` + |
|||
`${chalk.bold(_domain.aliases.length + " alias" + (_domain.aliases.length === 1 ? "" : "es"))} ` + |
|||
`will be removed. Run ${chalk.dim("`now alias ls`")} to list.\n` |
|||
); |
|||
} |
|||
|
|||
process.stdout.write(` ${chalk.bold.red('> Are you sure?')} ${chalk.gray('[y/N] ')}`) |
|||
|
|||
process.stdin.on('data', d => { |
|||
process.stdin.pause() |
|||
resolve(d.toString().trim()) |
|||
}).resume() |
|||
}) |
|||
process.stdout.write( |
|||
` ${chalk.bold.red("> Are you sure?")} ${chalk.gray("[y/N] ")}` |
|||
); |
|||
|
|||
process.stdin |
|||
.on("data", d => { |
|||
process.stdin.pause(); |
|||
resolve(d.toString().trim()); |
|||
}) |
|||
.resume(); |
|||
}); |
|||
} |
|||
|
|||
function findDomain(val, list) { |
|||
return list.find(d => { |
|||
if (d.uid === val) { |
|||
if (debug) { |
|||
console.log(`> [debug] matched domain ${d.uid} by uid`) |
|||
console.log(`> [debug] matched domain ${d.uid} by uid`); |
|||
} |
|||
|
|||
return true |
|||
return true; |
|||
} |
|||
|
|||
// match prefix
|
|||
if (d.name === toHost(val)) { |
|||
if (debug) { |
|||
console.log(`> [debug] matched domain ${d.uid} by name ${d.name}`) |
|||
console.log(`> [debug] matched domain ${d.uid} by name ${d.name}`); |
|||
} |
|||
|
|||
return true |
|||
return true; |
|||
} |
|||
|
|||
return false |
|||
}) |
|||
return false; |
|||
}); |
|||
} |
|||
|
@ -1,154 +1,163 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
// Packages
|
|||
const fs = require('fs-promise') |
|||
const minimist = require('minimist') |
|||
const chalk = require('chalk') |
|||
const table = require('text-table') |
|||
const ms = require('ms') |
|||
const fs = require("fs-promise"); |
|||
const minimist = require("minimist"); |
|||
const chalk = require("chalk"); |
|||
const table = require("text-table"); |
|||
const ms = require("ms"); |
|||
|
|||
// Ours
|
|||
const strlen = require('../lib/strlen') |
|||
const indent = require('../lib/indent') |
|||
const Now = require('../lib') |
|||
const login = require('../lib/login') |
|||
const cfg = require('../lib/cfg') |
|||
const {handleError, error} = require('../lib/error') |
|||
const logo = require('../lib/utils/output/logo') |
|||
const strlen = require("../lib/strlen"); |
|||
const indent = require("../lib/indent"); |
|||
const Now = require("../lib"); |
|||
const login = require("../lib/login"); |
|||
const cfg = require("../lib/cfg"); |
|||
const { handleError, error } = require("../lib/error"); |
|||
const logo = require("../lib/utils/output/logo"); |
|||
|
|||
const argv = minimist(process.argv.slice(2), { |
|||
string: ['config', 'token'], |
|||
boolean: ['help', 'debug'], |
|||
string: ["config", "token"], |
|||
boolean: ["help", "debug"], |
|||
alias: { |
|||
help: 'h', |
|||
config: 'c', |
|||
debug: 'd', |
|||
token: 't' |
|||
help: "h", |
|||
config: "c", |
|||
debug: "d", |
|||
token: "t" |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
const help = () => { |
|||
console.log(` |
|||
console.log( |
|||
` |
|||
${chalk.bold(`${logo} now list`)} [app] |
|||
|
|||
${chalk.dim('Options:')} |
|||
${chalk.dim("Options:")} |
|||
|
|||
-h, --help Output usage information |
|||
-c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file |
|||
-c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file |
|||
-d, --debug Debug mode [off] |
|||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token |
|||
-t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token |
|||
|
|||
${chalk.dim('Examples:')} |
|||
${chalk.dim("Examples:")} |
|||
|
|||
${chalk.gray('–')} List all deployments |
|||
${chalk.gray("–")} List all deployments |
|||
|
|||
${chalk.cyan('$ now ls')} |
|||
${chalk.cyan("$ now ls")} |
|||
|
|||
${chalk.gray('–')} List all deployments for the app ${chalk.dim('`my-app`')} |
|||
${chalk.gray("–")} List all deployments for the app ${chalk.dim("`my-app`")} |
|||
|
|||
${chalk.cyan('$ now ls my-app')} |
|||
${chalk.cyan("$ now ls my-app")} |
|||
|
|||
${chalk.dim('Alias:')} ls |
|||
`)
|
|||
} |
|||
${chalk.dim("Alias:")} ls |
|||
` |
|||
); |
|||
}; |
|||
|
|||
if (argv.help) { |
|||
help() |
|||
process.exit(0) |
|||
help(); |
|||
process.exit(0); |
|||
} |
|||
|
|||
const app = argv._[0] |
|||
const app = argv._[0]; |
|||
|
|||
// options
|
|||
const debug = argv.debug |
|||
const apiUrl = argv.url || 'https://api.zeit.co' |
|||
const debug = argv.debug; |
|||
const apiUrl = argv.url || "https://api.zeit.co"; |
|||
|
|||
if (argv.config) { |
|||
cfg.setConfigFile(argv.config) |
|||
cfg.setConfigFile(argv.config); |
|||
} |
|||
|
|||
const config = cfg.read() |
|||
const config = cfg.read(); |
|||
|
|||
Promise.resolve(argv.token || config.token || login(apiUrl)) |
|||
.then(async token => { |
|||
try { |
|||
await list(token) |
|||
} catch (err) { |
|||
error(`Unknown error: ${err}\n${err.stack}`) |
|||
process.exit(1) |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`) |
|||
process.exit(1) |
|||
}) |
|||
.then(async token => { |
|||
try { |
|||
await list(token); |
|||
} catch (err) { |
|||
error(`Unknown error: ${err}\n${err.stack}`); |
|||
process.exit(1); |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`); |
|||
process.exit(1); |
|||
}); |
|||
|
|||
async function list(token) { |
|||
const now = new Now(apiUrl, token, {debug}) |
|||
const start = new Date() |
|||
const now = new Now(apiUrl, token, { debug }); |
|||
const start = new Date(); |
|||
|
|||
let deployments |
|||
let deployments; |
|||
try { |
|||
deployments = await now.list(app) |
|||
deployments = await now.list(app); |
|||
} catch (err) { |
|||
handleError(err) |
|||
process.exit(1) |
|||
handleError(err); |
|||
process.exit(1); |
|||
} |
|||
|
|||
now.close() |
|||
now.close(); |
|||
|
|||
const apps = new Map() |
|||
const apps = new Map(); |
|||
|
|||
for (const dep of deployments) { |
|||
const deps = apps.get(dep.name) || [] |
|||
apps.set(dep.name, deps.concat(dep)) |
|||
const deps = apps.get(dep.name) || []; |
|||
apps.set(dep.name, deps.concat(dep)); |
|||
} |
|||
|
|||
const sorted = await sort([...apps]) |
|||
const current = Date.now() |
|||
|
|||
const text = sorted.map(([name, deps]) => { |
|||
const t = table(deps.map(({uid, url, created}) => { |
|||
const _url = url ? chalk.underline(`https://${url}`) : 'incomplete' |
|||
const time = chalk.gray(ms(current - created) + ' ago') |
|||
return [uid, _url, time] |
|||
}), {align: ['l', 'r', 'l'], hsep: ' '.repeat(6), stringLength: strlen}) |
|||
return chalk.bold(name) + '\n\n' + indent(t, 2) |
|||
}).join('\n\n') |
|||
const sorted = await sort([...apps]); |
|||
const current = Date.now(); |
|||
|
|||
const text = sorted |
|||
.map(([name, deps]) => { |
|||
const t = table( |
|||
deps.map(({ uid, url, created }) => { |
|||
const _url = url ? chalk.underline(`https://${url}`) : "incomplete"; |
|||
const time = chalk.gray(ms(current - created) + " ago"); |
|||
return [uid, _url, time]; |
|||
}), |
|||
{ align: ["l", "r", "l"], hsep: " ".repeat(6), stringLength: strlen } |
|||
); |
|||
return chalk.bold(name) + "\n\n" + indent(t, 2); |
|||
}) |
|||
.join("\n\n"); |
|||
|
|||
const elapsed = ms(new Date() - start) |
|||
console.log(`> ${deployments.length} deployment${deployments.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`) |
|||
const elapsed = ms(new Date() - start); |
|||
console.log( |
|||
`> ${deployments.length} deployment${deployments.length === 1 ? "" : "s"} found ${chalk.gray(`[${elapsed}]`)}` |
|||
); |
|||
|
|||
if (text) { |
|||
console.log('\n' + text + '\n') |
|||
console.log("\n" + text + "\n"); |
|||
} |
|||
} |
|||
|
|||
async function sort(apps) { |
|||
let pkg |
|||
let pkg; |
|||
try { |
|||
const json = await fs.readFile('package.json') |
|||
pkg = JSON.parse(json) |
|||
const json = await fs.readFile("package.json"); |
|||
pkg = JSON.parse(json); |
|||
} catch (err) { |
|||
pkg = {} |
|||
pkg = {}; |
|||
} |
|||
|
|||
return apps |
|||
.map(([name, deps]) => { |
|||
deps = deps.slice().sort((a, b) => { |
|||
return b.created - a.created |
|||
.map(([name, deps]) => { |
|||
deps = deps.slice().sort((a, b) => { |
|||
return b.created - a.created; |
|||
}); |
|||
return [name, deps]; |
|||
}) |
|||
return [name, deps] |
|||
}) |
|||
.sort(([nameA, depsA], [nameB, depsB]) => { |
|||
if (pkg.name === nameA) { |
|||
return -1 |
|||
} |
|||
.sort(([nameA, depsA], [nameB, depsB]) => { |
|||
if (pkg.name === nameA) { |
|||
return -1; |
|||
} |
|||
|
|||
if (pkg.name === nameB) { |
|||
return 1 |
|||
} |
|||
if (pkg.name === nameB) { |
|||
return 1; |
|||
} |
|||
|
|||
return depsB[0].created - depsA[0].created |
|||
}) |
|||
return depsB[0].created - depsA[0].created; |
|||
}); |
|||
} |
|||
|
@ -1,141 +1,143 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
// Packages
|
|||
const fs = require('fs-promise') |
|||
const minimist = require('minimist') |
|||
const chalk = require('chalk') |
|||
const opn = require('opn') |
|||
const fs = require("fs-promise"); |
|||
const minimist = require("minimist"); |
|||
const chalk = require("chalk"); |
|||
const opn = require("opn"); |
|||
|
|||
// Ours
|
|||
const Now = require('../lib') |
|||
const login = require('../lib/login') |
|||
const cfg = require('../lib/cfg') |
|||
const {handleError, error} = require('../lib/error') |
|||
const logo = require('../lib/utils/output/logo') |
|||
const Now = require("../lib"); |
|||
const login = require("../lib/login"); |
|||
const cfg = require("../lib/cfg"); |
|||
const { handleError, error } = require("../lib/error"); |
|||
const logo = require("../lib/utils/output/logo"); |
|||
|
|||
const argv = minimist(process.argv.slice(2), { |
|||
string: ['config', 'token'], |
|||
boolean: ['help', 'debug'], |
|||
string: ["config", "token"], |
|||
boolean: ["help", "debug"], |
|||
alias: { |
|||
help: 'h', |
|||
config: 'c', |
|||
debug: 'd', |
|||
token: 't' |
|||
help: "h", |
|||
config: "c", |
|||
debug: "d", |
|||
token: "t" |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
const help = () => { |
|||
console.log(` |
|||
console.log( |
|||
` |
|||
${chalk.bold(`${logo} now open`)} |
|||
|
|||
${chalk.dim('Options:')} |
|||
${chalk.dim("Options:")} |
|||
|
|||
-h, --help Output usage information |
|||
-c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file |
|||
-c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file |
|||
-d, --debug Debug mode [off] |
|||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token |
|||
-t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token |
|||
|
|||
${chalk.dim('Examples:')} |
|||
${chalk.dim("Examples:")} |
|||
|
|||
${chalk.gray('–')} Open latest deployment for current project |
|||
${chalk.gray("–")} Open latest deployment for current project |
|||
|
|||
${chalk.cyan('$ now open')} |
|||
${chalk.cyan("$ now open")} |
|||
|
|||
`)
|
|||
} |
|||
` |
|||
); |
|||
}; |
|||
|
|||
if (argv.help) { |
|||
help() |
|||
process.exit(0) |
|||
help(); |
|||
process.exit(0); |
|||
} |
|||
|
|||
const app = argv._[0] |
|||
const app = argv._[0]; |
|||
|
|||
// options
|
|||
const debug = argv.debug |
|||
const apiUrl = argv.url || 'https://api.zeit.co' |
|||
const debug = argv.debug; |
|||
const apiUrl = argv.url || "https://api.zeit.co"; |
|||
|
|||
if (argv.config) { |
|||
cfg.setConfigFile(argv.config) |
|||
cfg.setConfigFile(argv.config); |
|||
} |
|||
|
|||
const config = cfg.read() |
|||
const config = cfg.read(); |
|||
|
|||
Promise.resolve(argv.token || config.token || login(apiUrl)) |
|||
.then(async token => { |
|||
try { |
|||
await open(token) |
|||
} catch (err) { |
|||
error(`Unknown error: ${err}\n${err.stack}`) |
|||
process.exit(1) |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`) |
|||
process.exit(1) |
|||
}) |
|||
.then(async token => { |
|||
try { |
|||
await open(token); |
|||
} catch (err) { |
|||
error(`Unknown error: ${err}\n${err.stack}`); |
|||
process.exit(1); |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`); |
|||
process.exit(1); |
|||
}); |
|||
|
|||
async function open(token) { |
|||
const now = new Now(apiUrl, token, {debug}) |
|||
const now = new Now(apiUrl, token, { debug }); |
|||
|
|||
let deployments |
|||
let deployments; |
|||
try { |
|||
deployments = await now.list(app) |
|||
deployments = await now.list(app); |
|||
} catch (err) { |
|||
handleError(err) |
|||
process.exit(1) |
|||
handleError(err); |
|||
process.exit(1); |
|||
} |
|||
|
|||
now.close() |
|||
now.close(); |
|||
|
|||
const apps = new Map() |
|||
const apps = new Map(); |
|||
|
|||
for (const dep of deployments) { |
|||
const deps = apps.get(dep.name) || [] |
|||
apps.set(dep.name, deps.concat(dep)) |
|||
const deps = apps.get(dep.name) || []; |
|||
apps.set(dep.name, deps.concat(dep)); |
|||
} |
|||
|
|||
let pkg |
|||
let pkg; |
|||
try { |
|||
const json = await fs.readFile('package.json') |
|||
pkg = JSON.parse(json) |
|||
const json = await fs.readFile("package.json"); |
|||
pkg = JSON.parse(json); |
|||
} catch (err) { |
|||
pkg = {} |
|||
pkg = {}; |
|||
} |
|||
|
|||
const [currentProjectDeployments] = await getCurrentProjectDeployments([...apps], pkg) |
|||
const [currentProjectDeployments] = await getCurrentProjectDeployments( |
|||
[...apps], |
|||
pkg |
|||
); |
|||
|
|||
if (typeof currentProjectDeployments === 'undefined') { |
|||
console.log(`no deployments found for ${chalk.bold(pkg.name)}`) |
|||
process.exit(0) |
|||
if (typeof currentProjectDeployments === "undefined") { |
|||
console.log(`no deployments found for ${chalk.bold(pkg.name)}`); |
|||
process.exit(0); |
|||
} |
|||
|
|||
const sorted = await sortByCreation([...currentProjectDeployments]) |
|||
const latestDeploy = sorted[0] |
|||
const sorted = await sortByCreation([...currentProjectDeployments]); |
|||
const latestDeploy = sorted[0]; |
|||
|
|||
try { |
|||
const url = `https://${latestDeploy.url}` |
|||
const url = `https://${latestDeploy.url}`; |
|||
|
|||
console.log(`Opening the latest deployment for ${chalk.bold(pkg.name)}...`) |
|||
console.log(`Here's the URL: ${chalk.underline(url)}`) |
|||
console.log(`Opening the latest deployment for ${chalk.bold(pkg.name)}...`); |
|||
console.log(`Here's the URL: ${chalk.underline(url)}`); |
|||
|
|||
opn(url) |
|||
process.exit(0) |
|||
opn(url); |
|||
process.exit(0); |
|||
} catch (err) { |
|||
error(`Unknown error: ${err}\n${err.stack}`) |
|||
process.exit(1) |
|||
error(`Unknown error: ${err}\n${err.stack}`); |
|||
process.exit(1); |
|||
} |
|||
} |
|||
|
|||
async function getCurrentProjectDeployments(apps, pkg) { |
|||
return apps |
|||
.filter(app => pkg.name === app[0]) |
|||
.map(app => app[1]) |
|||
return apps.filter(app => pkg.name === app[0]).map(app => app[1]); |
|||
} |
|||
|
|||
async function sortByCreation(deps) { |
|||
return deps |
|||
.sort((depA, depB) => { |
|||
return depB.created - depA.created |
|||
}) |
|||
return deps.sort((depA, depB) => { |
|||
return depB.created - depA.created; |
|||
}); |
|||
} |
|||
|
@ -1,184 +1,202 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
// Packages
|
|||
const minimist = require('minimist') |
|||
const chalk = require('chalk') |
|||
const ms = require('ms') |
|||
const table = require('text-table') |
|||
const isURL = require('is-url') |
|||
const minimist = require("minimist"); |
|||
const chalk = require("chalk"); |
|||
const ms = require("ms"); |
|||
const table = require("text-table"); |
|||
const isURL = require("is-url"); |
|||
|
|||
// Ours
|
|||
const Now = require('../lib') |
|||
const login = require('../lib/login') |
|||
const cfg = require('../lib/cfg') |
|||
const {handleError, error} = require('../lib/error') |
|||
const logo = require('../lib/utils/output/logo') |
|||
const Now = require("../lib"); |
|||
const login = require("../lib/login"); |
|||
const cfg = require("../lib/cfg"); |
|||
const { handleError, error } = require("../lib/error"); |
|||
const logo = require("../lib/utils/output/logo"); |
|||
|
|||
const argv = minimist(process.argv.slice(2), { |
|||
string: ['config', 'token'], |
|||
boolean: ['help', 'debug', 'hard', 'yes'], |
|||
string: ["config", "token"], |
|||
boolean: ["help", "debug", "hard", "yes"], |
|||
alias: { |
|||
help: 'h', |
|||
config: 'c', |
|||
debug: 'd', |
|||
token: 't', |
|||
yes: 'y' |
|||
help: "h", |
|||
config: "c", |
|||
debug: "d", |
|||
token: "t", |
|||
yes: "y" |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
const ids = argv._ |
|||
const ids = argv._; |
|||
|
|||
// options
|
|||
const help = () => { |
|||
console.log(` |
|||
console.log( |
|||
` |
|||
${chalk.bold(`${logo} now remove`)} deploymentId|deploymentName [...deploymentId|deploymentName] |
|||
|
|||
${chalk.dim('Options:')} |
|||
${chalk.dim("Options:")} |
|||
|
|||
-h, --help Output usage information |
|||
-c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file |
|||
-c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file |
|||
-d, --debug Debug mode [off] |
|||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token |
|||
-t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token |
|||
-y, --yes Skip confirmation |
|||
|
|||
${chalk.dim('Examples:')} |
|||
${chalk.dim("Examples:")} |
|||
|
|||
${chalk.gray('–')} Remove a deployment identified by ${chalk.dim('`deploymentId`')}: |
|||
${chalk.gray("–")} Remove a deployment identified by ${chalk.dim("`deploymentId`")}: |
|||
|
|||
${chalk.cyan('$ now rm deploymentId')} |
|||
${chalk.cyan("$ now rm deploymentId")} |
|||
|
|||
${chalk.gray('–')} Remove all deployments with name ${chalk.dim('`my-app`')}: |
|||
${chalk.gray("–")} Remove all deployments with name ${chalk.dim("`my-app`")}: |
|||
|
|||
${chalk.cyan('$ now rm my-app')} |
|||
${chalk.cyan("$ now rm my-app")} |
|||
|
|||
${chalk.gray('–')} Remove two deployments with IDs ${chalk.dim('`eyWt6zuSdeus`')} and ${chalk.dim('`uWHoA9RQ1d1o`')}: |
|||
${chalk.gray("–")} Remove two deployments with IDs ${chalk.dim("`eyWt6zuSdeus`")} and ${chalk.dim("`uWHoA9RQ1d1o`")}: |
|||
|
|||
${chalk.cyan('$ now rm eyWt6zuSdeus uWHoA9RQ1d1o')} |
|||
${chalk.cyan("$ now rm eyWt6zuSdeus uWHoA9RQ1d1o")} |
|||
|
|||
${chalk.dim('Alias:')} rm |
|||
`)
|
|||
} |
|||
${chalk.dim("Alias:")} rm |
|||
` |
|||
); |
|||
}; |
|||
|
|||
if (argv.help || ids.length === 0) { |
|||
help() |
|||
process.exit(0) |
|||
help(); |
|||
process.exit(0); |
|||
} |
|||
|
|||
// options
|
|||
const debug = argv.debug |
|||
const apiUrl = argv.url || 'https://api.zeit.co' |
|||
const hard = argv.hard || false |
|||
const skipConfirmation = argv.yes || false |
|||
const debug = argv.debug; |
|||
const apiUrl = argv.url || "https://api.zeit.co"; |
|||
const hard = argv.hard || false; |
|||
const skipConfirmation = argv.yes || false; |
|||
|
|||
if (argv.config) { |
|||
cfg.setConfigFile(argv.config) |
|||
cfg.setConfigFile(argv.config); |
|||
} |
|||
|
|||
const config = cfg.read() |
|||
const config = cfg.read(); |
|||
|
|||
function readConfirmation(matches) { |
|||
return new Promise(resolve => { |
|||
process.stdout.write(`> The following deployment${matches.length === 1 ? '' : 's'} will be removed permanently:\n`) |
|||
process.stdout.write( |
|||
`> The following deployment${matches.length === 1 ? "" : "s"} will be removed permanently:\n` |
|||
); |
|||
|
|||
const tbl = table( |
|||
matches.map(depl => { |
|||
const time = chalk.gray(ms(new Date() - depl.created) + ' ago') |
|||
const url = depl.url ? chalk.underline(`https://${depl.url}`) : '' |
|||
return [depl.uid, url, time] |
|||
const time = chalk.gray(ms(new Date() - depl.created) + " ago"); |
|||
const url = depl.url ? chalk.underline(`https://${depl.url}`) : ""; |
|||
return [depl.uid, url, time]; |
|||
}), |
|||
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} |
|||
) |
|||
process.stdout.write(tbl + '\n') |
|||
{ align: ["l", "r", "l"], hsep: " ".repeat(6) } |
|||
); |
|||
process.stdout.write(tbl + "\n"); |
|||
|
|||
for (const depl of matches) { |
|||
for (const alias of depl.aliases) { |
|||
process.stdout.write( |
|||
`> ${chalk.yellow('Warning!')} Deployment ${chalk.bold(depl.uid)} ` + |
|||
`is an alias for ${chalk.underline(`https://${alias.alias}`)} and will be removed.\n` |
|||
) |
|||
`> ${chalk.yellow("Warning!")} Deployment ${chalk.bold(depl.uid)} ` + |
|||
`is an alias for ${chalk.underline(`https://${alias.alias}`)} and will be removed.\n` |
|||
); |
|||
} |
|||
} |
|||
|
|||
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[y/N] ')}`) |
|||
|
|||
process.stdin.on('data', d => { |
|||
process.stdin.pause() |
|||
resolve(d.toString().trim()) |
|||
}).resume() |
|||
}) |
|||
process.stdout.write( |
|||
`${chalk.bold.red("> Are you sure?")} ${chalk.gray("[y/N] ")}` |
|||
); |
|||
|
|||
process.stdin |
|||
.on("data", d => { |
|||
process.stdin.pause(); |
|||
resolve(d.toString().trim()); |
|||
}) |
|||
.resume(); |
|||
}); |
|||
} |
|||
|
|||
Promise.resolve(argv.token || config.token || login(apiUrl)) |
|||
.then(async token => { |
|||
try { |
|||
await remove(token) |
|||
} catch (err) { |
|||
error(`Unknown error: ${err}\n${err.stack}`) |
|||
process.exit(1) |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`) |
|||
process.exit(1) |
|||
}) |
|||
.then(async token => { |
|||
try { |
|||
await remove(token); |
|||
} catch (err) { |
|||
error(`Unknown error: ${err}\n${err.stack}`); |
|||
process.exit(1); |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`); |
|||
process.exit(1); |
|||
}); |
|||
|
|||
async function remove(token) { |
|||
const now = new Now(apiUrl, token, {debug}) |
|||
const now = new Now(apiUrl, token, { debug }); |
|||
|
|||
const deployments = await now.list() |
|||
const deployments = await now.list(); |
|||
|
|||
const matches = deployments.filter(d => { |
|||
return ids.find(id => { |
|||
// Normalize URL by removing slash from the end
|
|||
if (isURL(id) && id.slice(-1) === '/') { |
|||
id = id.slice(0, -1) |
|||
if (isURL(id) && id.slice(-1) === "/") { |
|||
id = id.slice(0, -1); |
|||
} |
|||
|
|||
// `url` should match the hostname of the deployment
|
|||
let u = id.replace(/^https:\/\//i, '') |
|||
let u = id.replace(/^https:\/\//i, ""); |
|||
|
|||
if (u.indexOf('.') === -1) { |
|||
if (u.indexOf(".") === -1) { |
|||
// `.now.sh` domain is implied if just the subdomain is given
|
|||
u += '.now.sh' |
|||
u += ".now.sh"; |
|||
} |
|||
|
|||
return d.uid === id || d.name === id || d.url === u |
|||
}) |
|||
}) |
|||
return d.uid === id || d.name === id || d.url === u; |
|||
}); |
|||
}); |
|||
|
|||
if (matches.length === 0) { |
|||
error(`Could not find any deployments matching ${ids.map(id => chalk.bold(`"${id}"`)).join(', ')}. Run ${chalk.dim(`\`now ls\``)} to list.`) |
|||
return process.exit(1) |
|||
error( |
|||
`Could not find any deployments matching ${ids |
|||
.map(id => chalk.bold(`"${id}"`)) |
|||
.join(", ")}. Run ${chalk.dim(`\`now ls\``)} to list.` |
|||
); |
|||
return process.exit(1); |
|||
} |
|||
|
|||
const aliases = await Promise.all(matches.map(depl => now.listAliases(depl.uid))) |
|||
const aliases = await Promise.all( |
|||
matches.map(depl => now.listAliases(depl.uid)) |
|||
); |
|||
for (let i = 0; i < matches.length; i++) { |
|||
matches[i].aliases = aliases[i] |
|||
matches[i].aliases = aliases[i]; |
|||
} |
|||
|
|||
try { |
|||
if (!skipConfirmation) { |
|||
const confirmation = (await readConfirmation(matches)).toLowerCase() |
|||
const confirmation = (await readConfirmation(matches)).toLowerCase(); |
|||
|
|||
if (confirmation !== 'y' && confirmation !== 'yes') { |
|||
console.log('\n> Aborted') |
|||
process.exit(0) |
|||
if (confirmation !== "y" && confirmation !== "yes") { |
|||
console.log("\n> Aborted"); |
|||
process.exit(0); |
|||
} |
|||
} |
|||
|
|||
const start = new Date() |
|||
const start = new Date(); |
|||
|
|||
await Promise.all(matches.map(depl => now.remove(depl.uid, {hard}))) |
|||
await Promise.all(matches.map(depl => now.remove(depl.uid, { hard }))); |
|||
|
|||
const elapsed = ms(new Date() - start) |
|||
console.log(`${chalk.cyan('> Success!')} [${elapsed}]`) |
|||
console.log(table(matches.map(depl => { |
|||
return [`Deployment ${chalk.bold(depl.uid)} removed`] |
|||
}))) |
|||
const elapsed = ms(new Date() - start); |
|||
console.log(`${chalk.cyan("> Success!")} [${elapsed}]`); |
|||
console.log( |
|||
table( |
|||
matches.map(depl => { |
|||
return [`Deployment ${chalk.bold(depl.uid)} removed`]; |
|||
}) |
|||
) |
|||
); |
|||
} catch (err) { |
|||
handleError(err) |
|||
process.exit(1) |
|||
handleError(err); |
|||
process.exit(1); |
|||
} |
|||
|
|||
now.close() |
|||
now.close(); |
|||
} |
|||
|
@ -1,233 +1,268 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
// Packages
|
|||
const chalk = require('chalk') |
|||
const table = require('text-table') |
|||
const minimist = require('minimist') |
|||
const ms = require('ms') |
|||
const chalk = require("chalk"); |
|||
const table = require("text-table"); |
|||
const minimist = require("minimist"); |
|||
const ms = require("ms"); |
|||
|
|||
// Ours
|
|||
const strlen = require('../lib/strlen') |
|||
const cfg = require('../lib/cfg') |
|||
const {handleError, error} = require('../lib/error') |
|||
const NowSecrets = require('../lib/secrets') |
|||
const login = require('../lib/login') |
|||
const exit = require('../lib/utils/exit') |
|||
const logo = require('../lib/utils/output/logo') |
|||
const strlen = require("../lib/strlen"); |
|||
const cfg = require("../lib/cfg"); |
|||
const { handleError, error } = require("../lib/error"); |
|||
const NowSecrets = require("../lib/secrets"); |
|||
const login = require("../lib/login"); |
|||
const exit = require("../lib/utils/exit"); |
|||
const logo = require("../lib/utils/output/logo"); |
|||
|
|||
const argv = minimist(process.argv.slice(2), { |
|||
string: ['config', 'token'], |
|||
boolean: ['help', 'debug', 'base64'], |
|||
string: ["config", "token"], |
|||
boolean: ["help", "debug", "base64"], |
|||
alias: { |
|||
help: 'h', |
|||
config: 'c', |
|||
debug: 'd', |
|||
base64: 'b', |
|||
token: 't' |
|||
help: "h", |
|||
config: "c", |
|||
debug: "d", |
|||
base64: "b", |
|||
token: "t" |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
const subcommand = argv._[0] |
|||
const subcommand = argv._[0]; |
|||
|
|||
// options
|
|||
const help = () => { |
|||
console.log(` |
|||
console.log( |
|||
` |
|||
${chalk.bold(`${logo} now secrets`)} <ls | add | rename | rm> <secret> |
|||
|
|||
${chalk.dim('Options:')} |
|||
${chalk.dim("Options:")} |
|||
|
|||
-h, --help Output usage information |
|||
-b, --base64 Treat value as base64-encoded |
|||
-c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file |
|||
-c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file |
|||
-d, --debug Debug mode [off] |
|||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token |
|||
-t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token |
|||
|
|||
${chalk.dim('Examples:')} |
|||
${chalk.dim("Examples:")} |
|||
|
|||
${chalk.gray('–')} Lists all your secrets: |
|||
${chalk.gray("–")} Lists all your secrets: |
|||
|
|||
${chalk.cyan('$ now secrets ls')} |
|||
${chalk.cyan("$ now secrets ls")} |
|||
|
|||
${chalk.gray('–')} Adds a new secret: |
|||
${chalk.gray("–")} Adds a new secret: |
|||
|
|||
${chalk.cyan('$ now secrets add my-secret "my value"')} |
|||
|
|||
${chalk.gray('–')} Once added, a secret's value can't be retrieved in plaintext anymore |
|||
${chalk.gray('–')} If the secret's value is more than one word, wrap it in quotes |
|||
${chalk.gray('–')} Actually, when in doubt, wrap your value in quotes |
|||
${chalk.gray("–")} Once added, a secret's value can't be retrieved in plaintext anymore |
|||
${chalk.gray("–")} If the secret's value is more than one word, wrap it in quotes |
|||
${chalk.gray("–")} Actually, when in doubt, wrap your value in quotes |
|||
|
|||
${chalk.gray('–')} Exposes a secret as an env variable: |
|||
${chalk.gray("–")} Exposes a secret as an env variable: |
|||
|
|||
${chalk.cyan(`$ now -e MY_SECRET=${chalk.bold('@my-secret')}`)} |
|||
${chalk.cyan(`$ now -e MY_SECRET=${chalk.bold("@my-secret")}`)} |
|||
|
|||
Notice the ${chalk.cyan.bold('`@`')} symbol which makes the value a secret reference. |
|||
Notice the ${chalk.cyan.bold("`@`")} symbol which makes the value a secret reference. |
|||
|
|||
${chalk.gray('–')} Renames a secret: |
|||
${chalk.gray("–")} Renames a secret: |
|||
|
|||
${chalk.cyan(`$ now secrets rename my-secret my-renamed-secret`)} |
|||
|
|||
${chalk.gray('–')} Removes a secret: |
|||
${chalk.gray("–")} Removes a secret: |
|||
|
|||
${chalk.cyan(`$ now secrets rm my-secret`)} |
|||
`)
|
|||
} |
|||
` |
|||
); |
|||
}; |
|||
|
|||
// options
|
|||
const debug = argv.debug |
|||
const apiUrl = argv.url || 'https://api.zeit.co' |
|||
const debug = argv.debug; |
|||
const apiUrl = argv.url || "https://api.zeit.co"; |
|||
|
|||
if (argv.config) { |
|||
cfg.setConfigFile(argv.config) |
|||
cfg.setConfigFile(argv.config); |
|||
} |
|||
|
|||
if (argv.help || !subcommand) { |
|||
help() |
|||
exit(0) |
|||
help(); |
|||
exit(0); |
|||
} else { |
|||
const config = cfg.read() |
|||
const config = cfg.read(); |
|||
|
|||
Promise.resolve(argv.token || config.token || login(apiUrl)) |
|||
.then(async token => { |
|||
try { |
|||
await run(token) |
|||
} catch (err) { |
|||
handleError(err) |
|||
exit(1) |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`) |
|||
exit(1) |
|||
}) |
|||
.then(async token => { |
|||
try { |
|||
await run(token); |
|||
} catch (err) { |
|||
handleError(err); |
|||
exit(1); |
|||
} |
|||
}) |
|||
.catch(e => { |
|||
error(`Authentication error – ${e.message}`); |
|||
exit(1); |
|||
}); |
|||
} |
|||
|
|||
async function run(token) { |
|||
const secrets = new NowSecrets(apiUrl, token, {debug}) |
|||
const args = argv._.slice(1) |
|||
const start = Date.now() |
|||
const secrets = new NowSecrets(apiUrl, token, { debug }); |
|||
const args = argv._.slice(1); |
|||
const start = Date.now(); |
|||
|
|||
if (subcommand === 'ls' || subcommand === 'list') { |
|||
if (subcommand === "ls" || subcommand === "list") { |
|||
if (args.length !== 0) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret ls`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now secret ls`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
|
|||
const list = await secrets.ls() |
|||
const elapsed = ms(new Date() - start) |
|||
const list = await secrets.ls(); |
|||
const elapsed = ms(new Date() - start); |
|||
|
|||
console.log(`> ${list.length} secret${list.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`) |
|||
console.log( |
|||
`> ${list.length} secret${list.length === 1 ? "" : "s"} found ${chalk.gray(`[${elapsed}]`)}` |
|||
); |
|||
|
|||
if (list.length > 0) { |
|||
const cur = Date.now() |
|||
const header = [['', 'id', 'name', 'created'].map(s => chalk.dim(s))] |
|||
const out = table(header.concat(list.map(secret => { |
|||
return [ |
|||
'', |
|||
secret.uid, |
|||
chalk.bold(secret.name), |
|||
chalk.gray(ms(cur - new Date(secret.created)) + ' ago') |
|||
] |
|||
})), {align: ['l', 'r', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen}) |
|||
const cur = Date.now(); |
|||
const header = [["", "id", "name", "created"].map(s => chalk.dim(s))]; |
|||
const out = table( |
|||
header.concat( |
|||
list.map(secret => { |
|||
return [ |
|||
"", |
|||
secret.uid, |
|||
chalk.bold(secret.name), |
|||
chalk.gray(ms(cur - new Date(secret.created)) + " ago") |
|||
]; |
|||
}) |
|||
), |
|||
{ |
|||
align: ["l", "r", "l", "l"], |
|||
hsep: " ".repeat(2), |
|||
stringLength: strlen |
|||
} |
|||
); |
|||
|
|||
if (out) { |
|||
console.log('\n' + out + '\n') |
|||
console.log("\n" + out + "\n"); |
|||
} |
|||
} |
|||
return secrets.close() |
|||
return secrets.close(); |
|||
} |
|||
|
|||
if (subcommand === 'rm' || subcommand === 'remove') { |
|||
if (subcommand === "rm" || subcommand === "remove") { |
|||
if (args.length !== 1) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rm <id | name>`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now secret rm <id | name>`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
const list = await secrets.ls() |
|||
const list = await secrets.ls(); |
|||
const theSecret = list.filter(secret => { |
|||
return secret.uid === args[0] || secret.name === args[0] |
|||
})[0] |
|||
return secret.uid === args[0] || secret.name === args[0]; |
|||
})[0]; |
|||
|
|||
if (theSecret) { |
|||
const yes = await readConfirmation(theSecret) |
|||
const yes = await readConfirmation(theSecret); |
|||
if (!yes) { |
|||
error('User abort') |
|||
return exit(0) |
|||
error("User abort"); |
|||
return exit(0); |
|||
} |
|||
} else { |
|||
error(`No secret found by id or name "${args[0]}"`) |
|||
return exit(1) |
|||
error(`No secret found by id or name "${args[0]}"`); |
|||
return exit(1); |
|||
} |
|||
|
|||
const secret = await secrets.rm(args[0]) |
|||
const elapsed = ms(new Date() - start) |
|||
console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(secret.name)} ${chalk.gray(`(${secret.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}`) |
|||
return secrets.close() |
|||
const secret = await secrets.rm(args[0]); |
|||
const elapsed = ms(new Date() - start); |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Secret ${chalk.bold(secret.name)} ${chalk.gray(`(${secret.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}` |
|||
); |
|||
return secrets.close(); |
|||
} |
|||
|
|||
if (subcommand === 'rename') { |
|||
if (subcommand === "rename") { |
|||
if (args.length !== 2) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rename <old-name> <new-name>`')}`) |
|||
return exit(1) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now secret rename <old-name> <new-name>`")}` |
|||
); |
|||
return exit(1); |
|||
} |
|||
const secret = await secrets.rename(args[0], args[1]) |
|||
const elapsed = ms(new Date() - start) |
|||
console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(secret.oldName)} ${chalk.gray(`(${secret.uid})`)} renamed to ${chalk.bold(args[1])} ${chalk.gray(`[${elapsed}]`)}`) |
|||
return secrets.close() |
|||
const secret = await secrets.rename(args[0], args[1]); |
|||
const elapsed = ms(new Date() - start); |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Secret ${chalk.bold(secret.oldName)} ${chalk.gray(`(${secret.uid})`)} renamed to ${chalk.bold(args[1])} ${chalk.gray(`[${elapsed}]`)}` |
|||
); |
|||
return secrets.close(); |
|||
} |
|||
|
|||
if (subcommand === 'add' || subcommand === 'set') { |
|||
if (subcommand === "add" || subcommand === "set") { |
|||
if (args.length !== 2) { |
|||
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret add <name> <value>`')}`) |
|||
error( |
|||
`Invalid number of arguments. Usage: ${chalk.cyan("`now secret add <name> <value>`")}` |
|||
); |
|||
|
|||
if (args.length > 2) { |
|||
const example = chalk.cyan(`$ now secret add ${args[0]}`) |
|||
console.log(`> If your secret has spaces, make sure to wrap it in quotes. Example: \n ${example} `) |
|||
const example = chalk.cyan(`$ now secret add ${args[0]}`); |
|||
console.log( |
|||
`> If your secret has spaces, make sure to wrap it in quotes. Example: \n ${example} ` |
|||
); |
|||
} |
|||
|
|||
return exit(1) |
|||
return exit(1); |
|||
} |
|||
|
|||
const [name, value_] = args |
|||
let value |
|||
const [name, value_] = args; |
|||
let value; |
|||
|
|||
if (argv.base64) { |
|||
value = {base64: value_} |
|||
value = { base64: value_ }; |
|||
} else { |
|||
value = value_ |
|||
value = value_; |
|||
} |
|||
|
|||
const secret = await secrets.add(name, value) |
|||
const elapsed = ms(new Date() - start) |
|||
const secret = await secrets.add(name, value); |
|||
const elapsed = ms(new Date() - start); |
|||
|
|||
console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(name.toLowerCase())} ${chalk.gray(`(${secret.uid})`)} added ${chalk.gray(`[${elapsed}]`)}`) |
|||
return secrets.close() |
|||
console.log( |
|||
`${chalk.cyan("> Success!")} Secret ${chalk.bold(name.toLowerCase())} ${chalk.gray(`(${secret.uid})`)} added ${chalk.gray(`[${elapsed}]`)}` |
|||
); |
|||
return secrets.close(); |
|||
} |
|||
|
|||
error('Please specify a valid subcommand: ls | add | rename | rm') |
|||
help() |
|||
exit(1) |
|||
error("Please specify a valid subcommand: ls | add | rename | rm"); |
|||
help(); |
|||
exit(1); |
|||
} |
|||
|
|||
process.on('uncaughtException', err => { |
|||
handleError(err) |
|||
exit(1) |
|||
}) |
|||
process.on("uncaughtException", err => { |
|||
handleError(err); |
|||
exit(1); |
|||
}); |
|||
|
|||
function readConfirmation(secret) { |
|||
return new Promise(resolve => { |
|||
const time = chalk.gray(ms(new Date() - new Date(secret.created)) + ' ago') |
|||
const tbl = table( |
|||
[[secret.uid, chalk.bold(secret.name), time]], |
|||
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} |
|||
) |
|||
|
|||
process.stdout.write('> The following secret will be removed permanently\n') |
|||
process.stdout.write(' ' + tbl + '\n') |
|||
|
|||
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[y/N] ')}`) |
|||
|
|||
process.stdin.on('data', d => { |
|||
process.stdin.pause() |
|||
resolve(d.toString().trim().toLowerCase() === 'y') |
|||
}).resume() |
|||
}) |
|||
const time = chalk.gray(ms(new Date() - new Date(secret.created)) + " ago"); |
|||
const tbl = table([[secret.uid, chalk.bold(secret.name), time]], { |
|||
align: ["l", "r", "l"], |
|||
hsep: " ".repeat(6) |
|||
}); |
|||
|
|||
process.stdout.write( |
|||
"> The following secret will be removed permanently\n" |
|||
); |
|||
process.stdout.write(" " + tbl + "\n"); |
|||
|
|||
process.stdout.write( |
|||
`${chalk.bold.red("> Are you sure?")} ${chalk.gray("[y/N] ")}` |
|||
); |
|||
|
|||
process.stdin |
|||
.on("data", d => { |
|||
process.stdin.pause(); |
|||
resolve(d.toString().trim().toLowerCase() === "y"); |
|||
}) |
|||
.resume(); |
|||
}); |
|||
} |
|||
|
@ -1,102 +1,102 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
// Native
|
|||
const {resolve} = require('path') |
|||
const { resolve } = require("path"); |
|||
|
|||
// Packages
|
|||
const nodeVersion = require('node-version') |
|||
const updateNotifier = require('update-notifier') |
|||
const chalk = require('chalk') |
|||
const nodeVersion = require("node-version"); |
|||
const updateNotifier = require("update-notifier"); |
|||
const chalk = require("chalk"); |
|||
|
|||
// Ours
|
|||
const {error} = require('../lib/error') |
|||
const pkg = require('../lib/pkg') |
|||
const { error } = require("../lib/error"); |
|||
const pkg = require("../lib/pkg"); |
|||
|
|||
// Throw an error if node version is too low
|
|||
if (nodeVersion.major < 6) { |
|||
error('Now requires at least version 6 of Node. Please upgrade!') |
|||
process.exit(1) |
|||
error("Now requires at least version 6 of Node. Please upgrade!"); |
|||
process.exit(1); |
|||
} |
|||
|
|||
if (!process.pkg) { |
|||
const notifier = updateNotifier({pkg}) |
|||
const update = notifier.update |
|||
const notifier = updateNotifier({ pkg }); |
|||
const update = notifier.update; |
|||
|
|||
if (update) { |
|||
let message = `Update available! ${chalk.red(update.current)} → ${chalk.green(update.latest)} \n` |
|||
message += `Run ${chalk.magenta('npm i -g now')} to update!\n` |
|||
message += `${chalk.magenta('Changelog:')} https://github.com/zeit/now-cli/releases/tag/${update.latest}` |
|||
let message = `Update available! ${chalk.red(update.current)} → ${chalk.green(update.latest)} \n`; |
|||
message += `Run ${chalk.magenta("npm i -g now")} to update!\n`; |
|||
message += `${chalk.magenta("Changelog:")} https://github.com/zeit/now-cli/releases/tag/${update.latest}`; |
|||
|
|||
notifier.notify({message}) |
|||
notifier.notify({ message }); |
|||
} |
|||
} |
|||
|
|||
// This command will be run if no other sub command is specified
|
|||
const defaultCommand = 'deploy' |
|||
const defaultCommand = "deploy"; |
|||
|
|||
const commands = new Set([ |
|||
defaultCommand, |
|||
'help', |
|||
'list', |
|||
'ls', |
|||
'rm', |
|||
'remove', |
|||
'alias', |
|||
'aliases', |
|||
'ln', |
|||
'domain', |
|||
'domains', |
|||
'dns', |
|||
'cert', |
|||
'certs', |
|||
'secret', |
|||
'secrets', |
|||
'cc', |
|||
'billing', |
|||
'upgrade', |
|||
'downgrade', |
|||
'open' |
|||
]) |
|||
"help", |
|||
"list", |
|||
"ls", |
|||
"rm", |
|||
"remove", |
|||
"alias", |
|||
"aliases", |
|||
"ln", |
|||
"domain", |
|||
"domains", |
|||
"dns", |
|||
"cert", |
|||
"certs", |
|||
"secret", |
|||
"secrets", |
|||
"cc", |
|||
"billing", |
|||
"upgrade", |
|||
"downgrade", |
|||
"open" |
|||
]); |
|||
|
|||
const aliases = new Map([ |
|||
['ls', 'list'], |
|||
['rm', 'remove'], |
|||
['ln', 'alias'], |
|||
['aliases', 'alias'], |
|||
['domain', 'domains'], |
|||
['cert', 'certs'], |
|||
['secret', 'secrets'], |
|||
['cc', 'billing'], |
|||
['downgrade', 'upgrade'] |
|||
]) |
|||
|
|||
let cmd = defaultCommand |
|||
const args = process.argv.slice(2) |
|||
const index = args.findIndex(a => commands.has(a)) |
|||
["ls", "list"], |
|||
["rm", "remove"], |
|||
["ln", "alias"], |
|||
["aliases", "alias"], |
|||
["domain", "domains"], |
|||
["cert", "certs"], |
|||
["secret", "secrets"], |
|||
["cc", "billing"], |
|||
["downgrade", "upgrade"] |
|||
]); |
|||
|
|||
let cmd = defaultCommand; |
|||
const args = process.argv.slice(2); |
|||
const index = args.findIndex(a => commands.has(a)); |
|||
|
|||
if (index > -1) { |
|||
cmd = args[index] |
|||
args.splice(index, 1) |
|||
cmd = args[index]; |
|||
args.splice(index, 1); |
|||
|
|||
if (cmd === 'help') { |
|||
if (cmd === "help") { |
|||
if (index < args.length && commands.has(args[index])) { |
|||
cmd = args[index] |
|||
args.splice(index, 1) |
|||
cmd = args[index]; |
|||
args.splice(index, 1); |
|||
} else { |
|||
cmd = defaultCommand |
|||
cmd = defaultCommand; |
|||
} |
|||
|
|||
args.unshift('--help') |
|||
args.unshift("--help"); |
|||
} |
|||
|
|||
cmd = aliases.get(cmd) || cmd |
|||
cmd = aliases.get(cmd) || cmd; |
|||
} |
|||
|
|||
const bin = resolve(__dirname, 'now-' + cmd + '.js') |
|||
const bin = resolve(__dirname, "now-" + cmd + ".js"); |
|||
|
|||
// Prepare process.argv for subcommand
|
|||
process.argv = process.argv.slice(0, 2).concat(args) |
|||
process.argv = process.argv.slice(0, 2).concat(args); |
|||
|
|||
// Load sub command
|
|||
// With custom parameter to make "pkg" happy
|
|||
require(bin, 'may-exclude') |
|||
require(bin, "may-exclude"); |
|||
|
@ -1,153 +1,156 @@ |
|||
// Native
|
|||
const EventEmitter = require('events') |
|||
const EventEmitter = require("events"); |
|||
|
|||
// Packages
|
|||
const ansi = require('ansi-escapes') |
|||
const io = require('socket.io-client') |
|||
const chalk = require('chalk') |
|||
const ansi = require("ansi-escapes"); |
|||
const io = require("socket.io-client"); |
|||
const chalk = require("chalk"); |
|||
|
|||
const {compare, deserialize} = require('./logs') |
|||
const { compare, deserialize } = require("./logs"); |
|||
|
|||
class Lines { |
|||
constructor(maxLines = 100) { |
|||
this.max = maxLines |
|||
this.buf = [] |
|||
this.max = maxLines; |
|||
this.buf = []; |
|||
} |
|||
|
|||
write(str) { |
|||
const {max, buf} = this |
|||
const { max, buf } = this; |
|||
|
|||
if (buf.length === max) { |
|||
process.stdout.write(ansi.eraseLines(max + 1)) |
|||
buf.shift() |
|||
buf.forEach(line => console.log(line)) |
|||
process.stdout.write(ansi.eraseLines(max + 1)); |
|||
buf.shift(); |
|||
buf.forEach(line => console.log(line)); |
|||
} |
|||
|
|||
buf.push(str) |
|||
console.log(str) |
|||
buf.push(str); |
|||
console.log(str); |
|||
} |
|||
|
|||
reset() { |
|||
this.buf = [] |
|||
this.buf = []; |
|||
} |
|||
} |
|||
|
|||
module.exports = class Logger extends EventEmitter { |
|||
constructor(host, {debug = false, quiet = false} = {}) { |
|||
super() |
|||
this.host = host |
|||
this.debug = debug |
|||
this.quiet = quiet |
|||
constructor(host, { debug = false, quiet = false } = {}) { |
|||
super(); |
|||
this.host = host; |
|||
this.debug = debug; |
|||
this.quiet = quiet; |
|||
|
|||
// readyState
|
|||
this.building = false |
|||
this.building = false; |
|||
|
|||
this.socket = io(`https://io.now.sh/states?host=${host}&v=2`) |
|||
this.socket.once('error', this.onSocketError.bind(this)) |
|||
this.socket.on('state', this.onState.bind(this)) |
|||
this.socket.on('logs', this.onLog.bind(this)) |
|||
this.socket.on('backend', this.onComplete.bind(this)) |
|||
this.socket = io(`https://io.now.sh/states?host=${host}&v=2`); |
|||
this.socket.once("error", this.onSocketError.bind(this)); |
|||
this.socket.on("state", this.onState.bind(this)); |
|||
this.socket.on("logs", this.onLog.bind(this)); |
|||
this.socket.on("backend", this.onComplete.bind(this)); |
|||
|
|||
this.lines = new Lines(10) |
|||
this.lines = new Lines(10); |
|||
|
|||
// log buffer
|
|||
this.buf = [] |
|||
this.buf = []; |
|||
} |
|||
|
|||
onState(state) { |
|||
// console.log(state)
|
|||
if (!state.id) { |
|||
console.error('> Deployment not found') |
|||
this.emit('error') |
|||
return |
|||
console.error("> Deployment not found"); |
|||
this.emit("error"); |
|||
return; |
|||
} |
|||
|
|||
if (state.error) { |
|||
this.emit('error', state) |
|||
return |
|||
this.emit("error", state); |
|||
return; |
|||
} |
|||
|
|||
if (state.backend) { |
|||
this.onComplete() |
|||
return |
|||
this.onComplete(); |
|||
return; |
|||
} |
|||
|
|||
if (state.logs) { |
|||
state.logs.forEach(this.onLog, this) |
|||
state.logs.forEach(this.onLog, this); |
|||
} |
|||
} |
|||
|
|||
onLog(log) { |
|||
if (!this.building) { |
|||
if (!this.quiet) { |
|||
console.log('> Building') |
|||
console.log("> Building"); |
|||
} |
|||
this.building = true |
|||
this.building = true; |
|||
} |
|||
|
|||
if (this.quiet) { |
|||
return |
|||
return; |
|||
} |
|||
|
|||
log = deserialize(log) |
|||
log = deserialize(log); |
|||
|
|||
const timer = setTimeout(() => { |
|||
this.buf.sort((a, b) => compare(a.log, b.log)) |
|||
const idx = this.buf.findIndex(b => b.log.id === log.id) + 1 |
|||
for (const b of this.buf.slice(0, idx)) { |
|||
clearTimeout(b.timer) |
|||
this.printLog(b.log) |
|||
} |
|||
this.buf = this.buf.slice(idx) |
|||
}, 300) |
|||
const timer = setTimeout( |
|||
() => { |
|||
this.buf.sort((a, b) => compare(a.log, b.log)); |
|||
const idx = this.buf.findIndex(b => b.log.id === log.id) + 1; |
|||
for (const b of this.buf.slice(0, idx)) { |
|||
clearTimeout(b.timer); |
|||
this.printLog(b.log); |
|||
} |
|||
this.buf = this.buf.slice(idx); |
|||
}, |
|||
300 |
|||
); |
|||
|
|||
this.buf.push({log, timer}) |
|||
this.buf.push({ log, timer }); |
|||
} |
|||
|
|||
onComplete() { |
|||
this.socket.disconnect() |
|||
this.socket.disconnect(); |
|||
|
|||
if (this.building) { |
|||
this.building = false |
|||
this.building = false; |
|||
} |
|||
|
|||
this.buf.sort((a, b) => compare(a.log, b.log)) |
|||
this.buf.sort((a, b) => compare(a.log, b.log)); |
|||
|
|||
// flush all buffer
|
|||
for (const b of this.buf) { |
|||
clearTimeout(b.timer) |
|||
this.printLog(b.log) |
|||
clearTimeout(b.timer); |
|||
this.printLog(b.log); |
|||
} |
|||
this.buf = [] |
|||
this.buf = []; |
|||
|
|||
this.emit('close') |
|||
this.emit("close"); |
|||
} |
|||
|
|||
onSocketError(err) { |
|||
if (this.debug) { |
|||
console.log(`> [debug] Socket error ${err}\n${err.stack}`) |
|||
console.log(`> [debug] Socket error ${err}\n${err.stack}`); |
|||
} |
|||
} |
|||
|
|||
printLog(log) { |
|||
const data = log.object ? JSON.stringify(log.object) : log.text |
|||
const data = log.object ? JSON.stringify(log.object) : log.text; |
|||
|
|||
if (log.type === 'command') { |
|||
console.log(`${chalk.gray('>')} ▲ ${data}`) |
|||
this.lines.reset() |
|||
} else if (log.type === 'stderr') { |
|||
data.split('\n').forEach(v => { |
|||
if (log.type === "command") { |
|||
console.log(`${chalk.gray(">")} ▲ ${data}`); |
|||
this.lines.reset(); |
|||
} else if (log.type === "stderr") { |
|||
data.split("\n").forEach(v => { |
|||
if (v.length > 0) { |
|||
console.error(chalk.gray(`> ${v}`)) |
|||
console.error(chalk.gray(`> ${v}`)); |
|||
} |
|||
}) |
|||
this.lines.reset() |
|||
} else if (log.type === 'stdout') { |
|||
data.split('\n').forEach(v => { |
|||
}); |
|||
this.lines.reset(); |
|||
} else if (log.type === "stdout") { |
|||
data.split("\n").forEach(v => { |
|||
if (v.length > 0) { |
|||
this.lines.write(`${chalk.gray('>')} ${v}`) |
|||
this.lines.write(`${chalk.gray(">")} ${v}`); |
|||
} |
|||
}) |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
@ -1,102 +1,101 @@ |
|||
// Ours
|
|||
const Now = require('../lib') |
|||
const Now = require("../lib"); |
|||
|
|||
module.exports = class Certs extends Now { |
|||
|
|||
ls() { |
|||
return this.retry(async (bail, attempt) => { |
|||
if (this._debug) { |
|||
console.time(`> [debug] #${attempt} GET now/certs`) |
|||
console.time(`> [debug] #${attempt} GET now/certs`); |
|||
} |
|||
|
|||
const res = await this._fetch('/now/certs') |
|||
const res = await this._fetch("/now/certs"); |
|||
|
|||
if (this._debug) { |
|||
console.timeEnd(`> [debug] #${attempt} GET now/certs`) |
|||
console.timeEnd(`> [debug] #${attempt} GET now/certs`); |
|||
} |
|||
|
|||
const body = await res.json() |
|||
return body.certs |
|||
}) |
|||
const body = await res.json(); |
|||
return body.certs; |
|||
}); |
|||
} |
|||
|
|||
create(cn) { |
|||
return this.createCert(cn) |
|||
return this.createCert(cn); |
|||
} |
|||
|
|||
renew(cn) { |
|||
return this.createCert(cn, {renew: true}) |
|||
return this.createCert(cn, { renew: true }); |
|||
} |
|||
|
|||
put(cn, crt, key, ca) { |
|||
return this.retry(async (bail, attempt) => { |
|||
if (this._debug) { |
|||
console.time(`> [debug] #${attempt} PUT now/certs`) |
|||
console.time(`> [debug] #${attempt} PUT now/certs`); |
|||
} |
|||
|
|||
const res = await this._fetch('/now/certs', { |
|||
method: 'PUT', |
|||
const res = await this._fetch("/now/certs", { |
|||
method: "PUT", |
|||
body: { |
|||
domains: [cn], |
|||
ca, |
|||
cert: crt, |
|||
key |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
if (this._debug) { |
|||
console.timeEnd(`> [debug] #${attempt} PUT now/certs`) |
|||
console.timeEnd(`> [debug] #${attempt} PUT now/certs`); |
|||
} |
|||
|
|||
if (res.status === 403) { |
|||
return bail(new Error('Unauthorized')) |
|||
return bail(new Error("Unauthorized")); |
|||
} |
|||
|
|||
const body = await res.json() |
|||
const body = await res.json(); |
|||
|
|||
if (res.status !== 200) { |
|||
if (res.status === 404 || res.status === 400) { |
|||
const err = new Error(body.error.message) |
|||
err.userError = true |
|||
return bail(err) |
|||
const err = new Error(body.error.message); |
|||
err.userError = true; |
|||
return bail(err); |
|||
} |
|||
|
|||
throw new Error(body.error.message) |
|||
throw new Error(body.error.message); |
|||
} |
|||
|
|||
return body |
|||
}) |
|||
return body; |
|||
}); |
|||
} |
|||
|
|||
delete(cn) { |
|||
return this.retry(async (bail, attempt) => { |
|||
if (this._debug) { |
|||
console.time(`> [debug] #${attempt} DELETE now/certs/${cn}`) |
|||
console.time(`> [debug] #${attempt} DELETE now/certs/${cn}`); |
|||
} |
|||
|
|||
const res = await this._fetch(`/now/certs/${cn}`, {method: 'DELETE'}) |
|||
const res = await this._fetch(`/now/certs/${cn}`, { method: "DELETE" }); |
|||
|
|||
if (this._debug) { |
|||
console.timeEnd(`> [debug] #${attempt} DELETE now/certs/${cn}`) |
|||
console.timeEnd(`> [debug] #${attempt} DELETE now/certs/${cn}`); |
|||
} |
|||
|
|||
if (res.status === 403) { |
|||
return bail(new Error('Unauthorized')) |
|||
return bail(new Error("Unauthorized")); |
|||
} |
|||
|
|||
const body = await res.json() |
|||
const body = await res.json(); |
|||
|
|||
if (res.status !== 200) { |
|||
if (res.status === 404 || res.status === 400) { |
|||
const err = new Error(body.error.message) |
|||
err.userError = true |
|||
return bail(err) |
|||
const err = new Error(body.error.message); |
|||
err.userError = true; |
|||
return bail(err); |
|||
} |
|||
|
|||
throw new Error(body.error.message) |
|||
throw new Error(body.error.message); |
|||
} |
|||
|
|||
return body |
|||
}) |
|||
return body; |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
|
@ -1,16 +1,16 @@ |
|||
// Packages
|
|||
const {copy: _copy} = require('copy-paste') |
|||
const { copy: _copy } = require("copy-paste"); |
|||
|
|||
function copy(text) { |
|||
return new Promise((resolve, reject) => { |
|||
_copy(text, err => { |
|||
if (err) { |
|||
return reject(err) |
|||
return reject(err); |
|||
} |
|||
|
|||
resolve() |
|||
}) |
|||
}) |
|||
resolve(); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
module.exports = copy |
|||
module.exports = copy; |
|||
|
@ -1,15 +1,15 @@ |
|||
// Packages
|
|||
const dns = require('dns') |
|||
const dns = require("dns"); |
|||
|
|||
function resolve4(host) { |
|||
return new Promise((resolve, reject) => { |
|||
return dns.resolve4(host, (err, answer) => { |
|||
if (err) { |
|||
return reject(err) |
|||
return reject(err); |
|||
} |
|||
|
|||
resolve(answer) |
|||
}) |
|||
}) |
|||
resolve(answer); |
|||
}); |
|||
}); |
|||
} |
|||
module.exports = resolve4 |
|||
module.exports = resolve4; |
|||
|
@ -1,130 +1,139 @@ |
|||
// Ours
|
|||
const Now = require('../lib') |
|||
const Now = require("../lib"); |
|||
|
|||
module.exports = class DomainRecords extends Now { |
|||
|
|||
async getRecord(id) { |
|||
const all = (await this.ls()).entries() |
|||
const all = (await this.ls()).entries(); |
|||
for (const [domain, records] of all) { |
|||
for (const record of records) { |
|||
if (record.id === id) { |
|||
record.domain = domain |
|||
return record |
|||
record.domain = domain; |
|||
return record; |
|||
} |
|||
} |
|||
} |
|||
return null |
|||
return null; |
|||
} |
|||
|
|||
async ls(dom) { |
|||
let domains |
|||
let domains; |
|||
|
|||
if (dom) { |
|||
domains = [dom] |
|||
domains = [dom]; |
|||
} else { |
|||
const ret = await this.listDomains() |
|||
domains = ret.filter(x => !x.isExternal).map(x => x.name).sort((a, b) => a.localeCompare(b)) |
|||
const ret = await this.listDomains(); |
|||
domains = ret |
|||
.filter(x => !x.isExternal) |
|||
.map(x => x.name) |
|||
.sort((a, b) => a.localeCompare(b)); |
|||
} |
|||
|
|||
const records = new Map() |
|||
const records = new Map(); |
|||
for (const domain of domains) { |
|||
const body = await this.retry(async (bail, attempt) => { |
|||
const url = `/domains/${domain}/records` |
|||
const url = `/domains/${domain}/records`; |
|||
if (this._debug) { |
|||
console.time(`> [debug] #${attempt} GET ${url}`) |
|||
console.time(`> [debug] #${attempt} GET ${url}`); |
|||
} |
|||
const res = await this._fetch(url) |
|||
const res = await this._fetch(url); |
|||
if (this._debug) { |
|||
console.timeEnd(`> [debug] #${attempt} GET ${url}`) |
|||
console.timeEnd(`> [debug] #${attempt} GET ${url}`); |
|||
} |
|||
const body = await res.json() |
|||
const body = await res.json(); |
|||
|
|||
if (res.status === 404 && body.code === 'not_found') { |
|||
return bail(new Error(body.message)) |
|||
if (res.status === 404 && body.code === "not_found") { |
|||
return bail(new Error(body.message)); |
|||
} else if (res.status !== 200) { |
|||
throw new Error(`Failed to get DNS records for domain "${domain}"`) |
|||
throw new Error(`Failed to get DNS records for domain "${domain}"`); |
|||
} |
|||
|
|||
return body |
|||
}) |
|||
records.set(domain, body.records.sort((a, b) => a.slug.localeCompare(b.slug))) |
|||
return body; |
|||
}); |
|||
records.set( |
|||
domain, |
|||
body.records.sort((a, b) => a.slug.localeCompare(b.slug)) |
|||
); |
|||
} |
|||
|
|||
return records |
|||
return records; |
|||
} |
|||
|
|||
create(domain, data) { |
|||
const url = `/domains/${domain}/records` |
|||
const url = `/domains/${domain}/records`; |
|||
|
|||
return this.retry(async (bail, attempt) => { |
|||
if (this._debug) { |
|||
console.time(`> [debug] #${attempt} POST ${url}`) |
|||
console.time(`> [debug] #${attempt} POST ${url}`); |
|||
} |
|||
const res = await this._fetch(url, { |
|||
method: 'POST', |
|||
method: "POST", |
|||
body: data |
|||
}) |
|||
}); |
|||
if (this._debug) { |
|||
console.timeEnd(`> [debug] #${attempt} POST ${url}`) |
|||
console.timeEnd(`> [debug] #${attempt} POST ${url}`); |
|||
} |
|||
|
|||
const body = await res.json() |
|||
const body = await res.json(); |
|||
if (res.status === 400) { |
|||
return bail(new Error(body.error ? body.error.message : 'Unknown error')) |
|||
return bail( |
|||
new Error(body.error ? body.error.message : "Unknown error") |
|||
); |
|||
} else if (res.status === 403) { |
|||
const err = new Error(`Not authorized to access the domain "${domain}"`) |
|||
err.userError = true |
|||
return bail(err) |
|||
const err = new Error( |
|||
`Not authorized to access the domain "${domain}"` |
|||
); |
|||
err.userError = true; |
|||
return bail(err); |
|||
} else if (res.status === 404) { |
|||
let err |
|||
let err; |
|||
|
|||
if (body.error.code === 'not_found') { |
|||
err = new Error(`The domain "${domain}" was not found`) |
|||
err.userError = true |
|||
return bail(err) |
|||
if (body.error.code === "not_found") { |
|||
err = new Error(`The domain "${domain}" was not found`); |
|||
err.userError = true; |
|||
return bail(err); |
|||
} |
|||
} |
|||
|
|||
if (res.status !== 200) { |
|||
throw new Error(body.error ? body.error.message : 'Unknown error') |
|||
throw new Error(body.error ? body.error.message : "Unknown error"); |
|||
} |
|||
|
|||
return body |
|||
}) |
|||
return body; |
|||
}); |
|||
} |
|||
|
|||
delete(domain, recordId) { |
|||
const url = `/domains/${domain}/records/${recordId}` |
|||
const url = `/domains/${domain}/records/${recordId}`; |
|||
|
|||
return this.retry(async (bail, attempt) => { |
|||
if (this._debug) { |
|||
console.time(`> [debug] #${attempt} DELETE ${url}`) |
|||
console.time(`> [debug] #${attempt} DELETE ${url}`); |
|||
} |
|||
const res = await this._fetch(url, {method: 'DELETE'}) |
|||
const res = await this._fetch(url, { method: "DELETE" }); |
|||
if (this._debug) { |
|||
console.timeEnd(`> [debug] #${attempt} DELETE ${url}`) |
|||
console.timeEnd(`> [debug] #${attempt} DELETE ${url}`); |
|||
} |
|||
|
|||
const body = await res.json() |
|||
const body = await res.json(); |
|||
if (res.status === 403) { |
|||
const err = new Error(`Not authorized to access domain ${domain}`) |
|||
err.userError = true |
|||
return bail(err) |
|||
const err = new Error(`Not authorized to access domain ${domain}`); |
|||
err.userError = true; |
|||
return bail(err); |
|||
} else if (res.status === 404) { |
|||
let err |
|||
let err; |
|||
|
|||
if (body.error.code === 'not_found') { |
|||
err = new Error(body.error.message) |
|||
err.userError = true |
|||
return bail(err) |
|||
if (body.error.code === "not_found") { |
|||
err = new Error(body.error.message); |
|||
err.userError = true; |
|||
return bail(err); |
|||
} |
|||
} |
|||
|
|||
if (res.status !== 200) { |
|||
throw new Error(body.error ? body.error.message : 'Unkown error') |
|||
throw new Error(body.error ? body.error.message : "Unkown error"); |
|||
} |
|||
|
|||
return body |
|||
}) |
|||
return body; |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
|
@ -1,77 +1,81 @@ |
|||
// Packages
|
|||
const chalk = require('chalk') |
|||
const chalk = require("chalk"); |
|||
|
|||
// Ours
|
|||
const Now = require('../lib') |
|||
const isZeitWorld = require('./is-zeit-world') |
|||
const {DNS_VERIFICATION_ERROR} = require('./errors') |
|||
const Now = require("../lib"); |
|||
const isZeitWorld = require("./is-zeit-world"); |
|||
const { DNS_VERIFICATION_ERROR } = require("./errors"); |
|||
|
|||
const domainRegex = /^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$/ |
|||
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 await this.listDomains() |
|||
return await this.listDomains(); |
|||
} |
|||
|
|||
async rm(name) { |
|||
return this.retry(async (bail, attempt) => { |
|||
if (this._debug) { |
|||
console.time(`> [debug] #${attempt} DELETE /domains/${name}`) |
|||
console.time(`> [debug] #${attempt} DELETE /domains/${name}`); |
|||
} |
|||
|
|||
const res = await this._fetch(`/domains/${name}`, {method: 'DELETE'}) |
|||
const res = await this._fetch(`/domains/${name}`, { method: "DELETE" }); |
|||
|
|||
if (this._debug) { |
|||
console.timeEnd(`> [debug] #${attempt} DELETE /domains/${name}`) |
|||
console.timeEnd(`> [debug] #${attempt} DELETE /domains/${name}`); |
|||
} |
|||
|
|||
if (res.status === 403) { |
|||
return bail(new Error('Unauthorized')) |
|||
return bail(new Error("Unauthorized")); |
|||
} |
|||
|
|||
if (res.status !== 200) { |
|||
const body = await res.json() |
|||
throw new Error(body.error.message) |
|||
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 |
|||
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}) |
|||
return this.setupDomain(domain, { isExternal }); |
|||
} |
|||
|
|||
let ns |
|||
let ns; |
|||
|
|||
try { |
|||
console.log('> Verifying nameservers…') |
|||
const res = await this.getNameservers(domain) |
|||
ns = res.nameservers |
|||
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 |
|||
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) |
|||
console.log(`> Verification ${chalk.bold("OK")}!`); |
|||
return this.setupDomain(domain); |
|||
} |
|||
|
|||
if (this._debug) { |
|||
console.log(`> [debug] Supplied domain "${domain}" has non-zeit nameservers`) |
|||
console.log( |
|||
`> [debug] Supplied domain "${domain}" has non-zeit nameservers` |
|||
); |
|||
} |
|||
|
|||
const err3 = new Error(DNS_VERIFICATION_ERROR) |
|||
err3.userError = true |
|||
throw err3 |
|||
const err3 = new Error(DNS_VERIFICATION_ERROR); |
|||
err3.userError = true; |
|||
throw err3; |
|||
} |
|||
|
|||
} |
|||
}; |
|||
|
@ -1,33 +1,37 @@ |
|||
// Packages
|
|||
const ms = require('ms') |
|||
const chalk = require('chalk') |
|||
const ms = require("ms"); |
|||
const chalk = require("chalk"); |
|||
|
|||
const error = require('./utils/output/error') |
|||
const error = require("./utils/output/error"); |
|||
|
|||
function handleError(err) { |
|||
if (err.status === 403) { |
|||
error('Authentication error. Run `now -L` or `now --login` to log-in again.') |
|||
error( |
|||
"Authentication error. Run `now -L` or `now --login` to log-in again." |
|||
); |
|||
} else if (err.status === 429) { |
|||
if (err.retryAfter === 'never') { |
|||
error(err.message) |
|||
if (err.retryAfter === "never") { |
|||
error(err.message); |
|||
} else if (err.retryAfter === null) { |
|||
error('Rate limit exceeded error. Please try later.') |
|||
error("Rate limit exceeded error. Please try later."); |
|||
} else { |
|||
error('Rate limit exceeded error. Try again in ' + |
|||
ms(err.retryAfter * 1000, {long: true}) + |
|||
', or upgrade your account by runnung ' + |
|||
`${chalk.gray('`')}${chalk.cyan('now upgrade')}${chalk.gray('`')}`) |
|||
error( |
|||
"Rate limit exceeded error. Try again in " + |
|||
ms(err.retryAfter * 1000, { long: true }) + |
|||
", or upgrade your account by runnung " + |
|||
`${chalk.gray("`")}${chalk.cyan("now upgrade")}${chalk.gray("`")}` |
|||
); |
|||
} |
|||
} else if (err.userError) { |
|||
error(err.message) |
|||
error(err.message); |
|||
} else if (err.status === 500) { |
|||
error('Unexpected server error. Please retry.') |
|||
error("Unexpected server error. Please retry."); |
|||
} else { |
|||
error(`Unexpected error. Please try later. (${err.message})`) |
|||
error(`Unexpected error. Please try later. (${err.message})`); |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
handleError, |
|||
error |
|||
} |
|||
}; |
|||
|
@ -1,17 +1,17 @@ |
|||
// Packages
|
|||
const chalk = require('chalk') |
|||
const chalk = require("chalk"); |
|||
|
|||
const DNS_VERIFICATION_ERROR = `Please make sure that your nameservers point to ${chalk.underline('zeit.world')}.
|
|||
> Examples: (full list at ${chalk.underline('https://zeit.world')}) |
|||
> ${chalk.gray('-')} ${chalk.underline('california.zeit.world')} ${chalk.dim('173.255.215.107')} |
|||
> ${chalk.gray('-')} ${chalk.underline('newark.zeit.world')} ${chalk.dim('173.255.231.87')} |
|||
> ${chalk.gray('-')} ${chalk.underline('london.zeit.world')} ${chalk.dim('178.62.47.76')} |
|||
> ${chalk.gray('-')} ${chalk.underline('singapore.zeit.world')} ${chalk.dim('119.81.97.170')}` |
|||
const DNS_VERIFICATION_ERROR = `Please make sure that your nameservers point to ${chalk.underline("zeit.world")}.
|
|||
> Examples: (full list at ${chalk.underline("https://zeit.world")}) |
|||
> ${chalk.gray("-")} ${chalk.underline("california.zeit.world")} ${chalk.dim("173.255.215.107")} |
|||
> ${chalk.gray("-")} ${chalk.underline("newark.zeit.world")} ${chalk.dim("173.255.231.87")} |
|||
> ${chalk.gray("-")} ${chalk.underline("london.zeit.world")} ${chalk.dim("178.62.47.76")} |
|||
> ${chalk.gray("-")} ${chalk.underline("singapore.zeit.world")} ${chalk.dim("119.81.97.170")}`;
|
|||
|
|||
const DOMAIN_VERIFICATION_ERROR = DNS_VERIFICATION_ERROR + |
|||
`\n> Alternatively, ensure it resolves to ${chalk.underline('alias.zeit.co')} via ${chalk.dim('CNAME')} / ${chalk.dim('ALIAS')}.` |
|||
`\n> Alternatively, ensure it resolves to ${chalk.underline("alias.zeit.co")} via ${chalk.dim("CNAME")} / ${chalk.dim("ALIAS")}.`; |
|||
|
|||
module.exports = { |
|||
DNS_VERIFICATION_ERROR, |
|||
DOMAIN_VERIFICATION_ERROR |
|||
} |
|||
}; |
|||
|
@ -1,211 +1,209 @@ |
|||
// Native
|
|||
const path = require('path') |
|||
const url = require('url') |
|||
const childProcess = require('child_process') |
|||
const path = require("path"); |
|||
const url = require("url"); |
|||
const childProcess = require("child_process"); |
|||
|
|||
// Packages
|
|||
const fs = require('fs-promise') |
|||
const download = require('download') |
|||
const tmp = require('tmp-promise') |
|||
const isURL = require('is-url') |
|||
|
|||
const cloneRepo = (parts, tmpDir) => new Promise((resolve, reject) => { |
|||
let host |
|||
|
|||
switch (parts.type) { |
|||
case 'GitLab': |
|||
host = `gitlab.com` |
|||
break |
|||
case 'Bitbucket': |
|||
host = `bitbucket.org` |
|||
break |
|||
default: |
|||
host = `github.com` |
|||
} |
|||
const fs = require("fs-promise"); |
|||
const download = require("download"); |
|||
const tmp = require("tmp-promise"); |
|||
const isURL = require("is-url"); |
|||
|
|||
const cloneRepo = (parts, tmpDir) => |
|||
new Promise((resolve, reject) => { |
|||
let host; |
|||
|
|||
switch (parts.type) { |
|||
case "GitLab": |
|||
host = `gitlab.com`; |
|||
break; |
|||
case "Bitbucket": |
|||
host = `bitbucket.org`; |
|||
break; |
|||
default: |
|||
host = `github.com`; |
|||
} |
|||
|
|||
const url = `https://${host}/${parts.main}` |
|||
const ref = parts.ref || (parts.type === 'Bitbucket' ? 'default' : 'master') |
|||
const cmd = `git clone ${url} --single-branch ${ref}` |
|||
const url = `https://${host}/${parts.main}`; |
|||
const ref = parts.ref || |
|||
(parts.type === "Bitbucket" ? "default" : "master"); |
|||
const cmd = `git clone ${url} --single-branch ${ref}`; |
|||
|
|||
childProcess.exec(cmd, {cwd: tmpDir.path}, (err, stdout) => { |
|||
if (err) { |
|||
reject(err) |
|||
} |
|||
childProcess.exec(cmd, { cwd: tmpDir.path }, (err, stdout) => { |
|||
if (err) { |
|||
reject(err); |
|||
} |
|||
|
|||
resolve(stdout) |
|||
}) |
|||
}) |
|||
resolve(stdout); |
|||
}); |
|||
}); |
|||
|
|||
const renameRepoDir = async (pathParts, tmpDir) => { |
|||
const tmpContents = await fs.readdir(tmpDir.path) |
|||
const tmpContents = await fs.readdir(tmpDir.path); |
|||
|
|||
const oldTemp = path.join(tmpDir.path, tmpContents[0]) |
|||
const newTemp = path.join(tmpDir.path, pathParts.main.replace('/', '-')) |
|||
const oldTemp = path.join(tmpDir.path, tmpContents[0]); |
|||
const newTemp = path.join(tmpDir.path, pathParts.main.replace("/", "-")); |
|||
|
|||
await fs.rename(oldTemp, newTemp) |
|||
tmpDir.path = newTemp |
|||
await fs.rename(oldTemp, newTemp); |
|||
tmpDir.path = newTemp; |
|||
|
|||
return tmpDir |
|||
} |
|||
return tmpDir; |
|||
}; |
|||
|
|||
const downloadRepo = async repoPath => { |
|||
const pathParts = gitPathParts(repoPath) |
|||
const pathParts = gitPathParts(repoPath); |
|||
|
|||
const tmpDir = await tmp.dir({ |
|||
// We'll remove it manually once deployment is done
|
|||
keep: true, |
|||
// Recursively remove directory when calling respective method
|
|||
unsafeCleanup: true |
|||
}) |
|||
}); |
|||
|
|||
let gitInstalled = true |
|||
let gitInstalled = true; |
|||
|
|||
try { |
|||
await cloneRepo(pathParts, tmpDir) |
|||
await cloneRepo(pathParts, tmpDir); |
|||
} catch (err) { |
|||
gitInstalled = false |
|||
gitInstalled = false; |
|||
} |
|||
|
|||
if (gitInstalled) { |
|||
return await renameRepoDir(pathParts, tmpDir) |
|||
return await renameRepoDir(pathParts, tmpDir); |
|||
} |
|||
|
|||
let url |
|||
let url; |
|||
|
|||
switch (pathParts.type) { |
|||
case 'GitLab': { |
|||
const ref = pathParts.ref ? `?ref=${pathParts.ref}` : '' |
|||
url = `https://gitlab.com/${pathParts.main}/repository/archive.tar` + ref |
|||
break |
|||
case "GitLab": { |
|||
const ref = pathParts.ref ? `?ref=${pathParts.ref}` : ""; |
|||
url = `https://gitlab.com/${pathParts.main}/repository/archive.tar` + ref; |
|||
break; |
|||
} |
|||
case 'Bitbucket': |
|||
url = `https://bitbucket.org/${pathParts.main}/get/${pathParts.ref || 'default'}.zip` |
|||
break |
|||
case "Bitbucket": |
|||
url = `https://bitbucket.org/${pathParts.main}/get/${pathParts.ref || "default"}.zip`; |
|||
break; |
|||
default: |
|||
url = `https://api.github.com/repos/${pathParts.main}/tarball/${pathParts.ref}` |
|||
url = `https://api.github.com/repos/${pathParts.main}/tarball/${pathParts.ref}`; |
|||
} |
|||
|
|||
try { |
|||
await download(url, tmpDir.path, { |
|||
extract: true |
|||
}) |
|||
}); |
|||
} catch (err) { |
|||
tmpDir.cleanup() |
|||
return false |
|||
tmpDir.cleanup(); |
|||
return false; |
|||
} |
|||
|
|||
return await renameRepoDir(pathParts, tmpDir) |
|||
} |
|||
return await renameRepoDir(pathParts, tmpDir); |
|||
}; |
|||
|
|||
const capitalizePlatform = name => { |
|||
const names = { |
|||
github: 'GitHub', |
|||
gitlab: 'GitLab', |
|||
bitbucket: 'Bitbucket' |
|||
} |
|||
github: "GitHub", |
|||
gitlab: "GitLab", |
|||
bitbucket: "Bitbucket" |
|||
}; |
|||
|
|||
return names[name] |
|||
} |
|||
return names[name]; |
|||
}; |
|||
|
|||
const splittedURL = fullURL => { |
|||
const parsedURL = url.parse(fullURL) |
|||
const pathParts = parsedURL.path.split('/') |
|||
const parsedURL = url.parse(fullURL); |
|||
const pathParts = parsedURL.path.split("/"); |
|||
|
|||
pathParts.shift() |
|||
pathParts.shift(); |
|||
|
|||
// Set path to repo...
|
|||
const main = pathParts[0] + '/' + pathParts[1] |
|||
const main = pathParts[0] + "/" + pathParts[1]; |
|||
|
|||
// ...and then remove it from the parts
|
|||
pathParts.splice(0, 2) |
|||
pathParts.splice(0, 2); |
|||
|
|||
// Assign Git reference
|
|||
let ref = pathParts.length >= 2 ? pathParts[1] : '' |
|||
let ref = pathParts.length >= 2 ? pathParts[1] : ""; |
|||
|
|||
// Firstly be sure that we haven know the ref type
|
|||
if (pathParts[0]) { |
|||
// Then shorten the SHA of the commit
|
|||
if (pathParts[0] === 'commit' || pathParts[0] === 'commits') { |
|||
ref = ref.substring(0, 7) |
|||
if (pathParts[0] === "commit" || pathParts[0] === "commits") { |
|||
ref = ref.substring(0, 7); |
|||
} |
|||
} |
|||
|
|||
// We're deploying master by default,
|
|||
// so there's no need to indicate it explicitly
|
|||
if (ref === 'master') { |
|||
ref = '' |
|||
if (ref === "master") { |
|||
ref = ""; |
|||
} |
|||
|
|||
return { |
|||
main, |
|||
ref, |
|||
type: capitalizePlatform(parsedURL.host.split('.')[0]) |
|||
} |
|||
} |
|||
type: capitalizePlatform(parsedURL.host.split(".")[0]) |
|||
}; |
|||
}; |
|||
|
|||
const gitPathParts = main => { |
|||
let ref = '' |
|||
let ref = ""; |
|||
|
|||
if (isURL(main)) { |
|||
return splittedURL(main) |
|||
return splittedURL(main); |
|||
} |
|||
|
|||
if (main.split('/')[1].includes('#')) { |
|||
const parts = main.split('#') |
|||
if (main.split("/")[1].includes("#")) { |
|||
const parts = main.split("#"); |
|||
|
|||
ref = parts[1] |
|||
main = parts[0] |
|||
ref = parts[1]; |
|||
main = parts[0]; |
|||
} |
|||
|
|||
return { |
|||
main, |
|||
ref, |
|||
type: capitalizePlatform('github') |
|||
} |
|||
} |
|||
type: capitalizePlatform("github") |
|||
}; |
|||
}; |
|||
|
|||
const isRepoPath = path => { |
|||
if (!path) { |
|||
return false |
|||
return false; |
|||
} |
|||
|
|||
const allowedHosts = [ |
|||
'github.com', |
|||
'gitlab.com', |
|||
'bitbucket.org' |
|||
] |
|||
const allowedHosts = ["github.com", "gitlab.com", "bitbucket.org"]; |
|||
|
|||
if (isURL(path)) { |
|||
const urlParts = url.parse(path) |
|||
const slashSplitted = urlParts.path.split('/').filter(n => n) |
|||
const notBare = slashSplitted.length >= 2 |
|||
const urlParts = url.parse(path); |
|||
const slashSplitted = urlParts.path.split("/").filter(n => n); |
|||
const notBare = slashSplitted.length >= 2; |
|||
|
|||
if (allowedHosts.includes(urlParts.host) && notBare) { |
|||
return true |
|||
return true; |
|||
} |
|||
|
|||
return 'no-valid-url' |
|||
return "no-valid-url"; |
|||
} |
|||
|
|||
return /[^\s\\]\/[^\s\\]/g.test(path) |
|||
} |
|||
return /[^\s\\]\/[^\s\\]/g.test(path); |
|||
}; |
|||
|
|||
const fromGit = async (path, debug) => { |
|||
let tmpDir = false |
|||
let tmpDir = false; |
|||
|
|||
try { |
|||
tmpDir = await downloadRepo(path) |
|||
tmpDir = await downloadRepo(path); |
|||
} catch (err) { |
|||
if (debug) { |
|||
console.log(`Could not download "${path}" repo from GitHub`) |
|||
console.log(`Could not download "${path}" repo from GitHub`); |
|||
} |
|||
} |
|||
|
|||
return tmpDir |
|||
} |
|||
return tmpDir; |
|||
}; |
|||
|
|||
module.exports = { |
|||
gitPathParts, |
|||
isRepoPath, |
|||
fromGit |
|||
} |
|||
}; |
|||
|
@ -1,5 +1,5 @@ |
|||
function indent(text, n) { |
|||
return text.split('\n').map(l => ' '.repeat(n) + l).join('\n') |
|||
return text.split("\n").map(l => " ".repeat(n) + l).join("\n"); |
|||
} |
|||
|
|||
module.exports = indent |
|||
module.exports = indent; |
|||
|
File diff suppressed because it is too large
@ -1,116 +1,125 @@ |
|||
// Native
|
|||
const os = require('os') |
|||
const os = require("os"); |
|||
|
|||
// Packages
|
|||
const {stringify: stringifyQuery} = require('querystring') |
|||
const chalk = require('chalk') |
|||
const fetch = require('node-fetch') |
|||
const {validate} = require('email-validator') |
|||
const readEmail = require('email-prompt') |
|||
const ora = require('ora') |
|||
const { stringify: stringifyQuery } = require("querystring"); |
|||
const chalk = require("chalk"); |
|||
const fetch = require("node-fetch"); |
|||
const { validate } = require("email-validator"); |
|||
const readEmail = require("email-prompt"); |
|||
const ora = require("ora"); |
|||
|
|||
// Ours
|
|||
const pkg = require('./pkg') |
|||
const ua = require('./ua') |
|||
const cfg = require('./cfg') |
|||
const info = require('./utils/output/info') |
|||
const promptBool = require('./utils/input/prompt-bool') |
|||
const pkg = require("./pkg"); |
|||
const ua = require("./ua"); |
|||
const cfg = require("./cfg"); |
|||
const info = require("./utils/output/info"); |
|||
const promptBool = require("./utils/input/prompt-bool"); |
|||
|
|||
async function getVerificationData(url, email) { |
|||
const tokenName = `Now CLI ${os.platform()}-${os.arch()} ${pkg.version} (${os.hostname()})` |
|||
const data = JSON.stringify({email, tokenName}) |
|||
const tokenName = `Now CLI ${os.platform()}-${os.arch()} ${pkg.version} (${os.hostname()})`; |
|||
const data = JSON.stringify({ email, tokenName }); |
|||
const res = await fetch(`${url}/now/registration`, { |
|||
method: 'POST', |
|||
method: "POST", |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
'Content-Length': Buffer.byteLength(data), |
|||
'User-Agent': ua |
|||
"Content-Type": "application/json", |
|||
"Content-Length": Buffer.byteLength(data), |
|||
"User-Agent": ua |
|||
}, |
|||
body: data |
|||
}) |
|||
}); |
|||
|
|||
if (res.status !== 200) { |
|||
throw new Error('Verification error') |
|||
throw new Error("Verification error"); |
|||
} |
|||
|
|||
const body = await res.json() |
|||
return body |
|||
const body = await res.json(); |
|||
return body; |
|||
} |
|||
|
|||
async function verify(url, email, verificationToken) { |
|||
const query = { |
|||
email, |
|||
token: verificationToken |
|||
} |
|||
|
|||
const res = await fetch(`${url}/now/registration/verify?${stringifyQuery(query)}`, { |
|||
headers: {'User-Agent': ua} |
|||
}) |
|||
const body = await res.json() |
|||
return body.token |
|||
}; |
|||
|
|||
const res = await fetch( |
|||
`${url}/now/registration/verify?${stringifyQuery(query)}`, |
|||
{ |
|||
headers: { "User-Agent": ua } |
|||
} |
|||
); |
|||
const body = await res.json(); |
|||
return body.token; |
|||
} |
|||
|
|||
function sleep(ms) { |
|||
return new Promise(resolve => { |
|||
setTimeout(resolve, ms) |
|||
}) |
|||
setTimeout(resolve, ms); |
|||
}); |
|||
} |
|||
|
|||
async function register(url, {retryEmail = false} = {}) { |
|||
let email |
|||
async function register(url, { retryEmail = false } = {}) { |
|||
let email; |
|||
try { |
|||
email = await readEmail({invalid: retryEmail}) |
|||
email = await readEmail({ invalid: retryEmail }); |
|||
} catch (err) { |
|||
process.stdout.write('\n') |
|||
throw err |
|||
process.stdout.write("\n"); |
|||
throw err; |
|||
} |
|||
|
|||
process.stdout.write('\n') |
|||
process.stdout.write("\n"); |
|||
|
|||
info(`By continuing, you declare that you agree with ${chalk.bold('https://zeit.co/terms')} and ${chalk.bold('https://zeit.co/privacy.')}`) |
|||
if (!await promptBool('Continue?')) { |
|||
info('Aborted.') |
|||
process.exit() // eslint-disable-line unicorn/no-process-exit
|
|||
info( |
|||
`By continuing, you declare that you agree with ${chalk.bold("https://zeit.co/terms")} and ${chalk.bold("https://zeit.co/privacy.")}` |
|||
); |
|||
if (!await promptBool("Continue?")) { |
|||
info("Aborted."); |
|||
process.exit(); // eslint-disable-line unicorn/no-process-exit
|
|||
} |
|||
|
|||
if (!validate(email)) { |
|||
return register(url, {retryEmail: true}) |
|||
return register(url, { retryEmail: true }); |
|||
} |
|||
|
|||
const {token, securityCode} = await getVerificationData(url, email) |
|||
console.log(`> Please follow the link sent to ${chalk.bold(email)} to log in.`) |
|||
const { token, securityCode } = await getVerificationData(url, email); |
|||
console.log( |
|||
`> Please follow the link sent to ${chalk.bold(email)} to log in.` |
|||
); |
|||
|
|||
if (securityCode) { |
|||
console.log(`> Verify that the provided security code in the email matches ${chalk.cyan(chalk.bold(securityCode))}.`) |
|||
console.log( |
|||
`> Verify that the provided security code in the email matches ${chalk.cyan(chalk.bold(securityCode))}.` |
|||
); |
|||
} |
|||
|
|||
process.stdout.write('\n') |
|||
process.stdout.write("\n"); |
|||
|
|||
const spinner = ora({ |
|||
text: 'Waiting for confirmation...', |
|||
color: 'black' |
|||
}).start() |
|||
text: "Waiting for confirmation...", |
|||
color: "black" |
|||
}).start(); |
|||
|
|||
let final |
|||
let final; |
|||
|
|||
do { |
|||
await sleep(2500) |
|||
await sleep(2500); |
|||
|
|||
try { |
|||
final = await verify(url, email, token) |
|||
final = await verify(url, email, token); |
|||
} catch (err) {} |
|||
} while (!final) |
|||
} while (!final); |
|||
|
|||
spinner.text = 'Confirmed email address!' |
|||
spinner.stopAndPersist('✔') |
|||
spinner.text = "Confirmed email address!"; |
|||
spinner.stopAndPersist("✔"); |
|||
|
|||
process.stdout.write('\n') |
|||
process.stdout.write("\n"); |
|||
|
|||
return {email, token: final} |
|||
return { email, token: final }; |
|||
} |
|||
|
|||
module.exports = async function (url) { |
|||
const loginData = await register(url) |
|||
cfg.merge(loginData) |
|||
return loginData.token |
|||
} |
|||
module.exports = async function(url) { |
|||
const loginData = await register(url); |
|||
cfg.merge(loginData); |
|||
return loginData.token; |
|||
}; |
|||
|
@ -1,12 +1,12 @@ |
|||
exports.compare = function (a, b) { |
|||
exports.compare = function(a, b) { |
|||
return a.serial.localeCompare(b.serial) || |
|||
// for the case serials are a same value on old logs
|
|||
a.created.getTime() - b.created.getTime() |
|||
} |
|||
a.created.getTime() - b.created.getTime(); |
|||
}; |
|||
|
|||
exports.deserialize = function (log) { |
|||
exports.deserialize = function(log) { |
|||
return Object.assign({}, log, { |
|||
data: new Date(log.date), |
|||
created: new Date(log.created) |
|||
}) |
|||
} |
|||
}); |
|||
}; |
|||
|
@ -1,8 +1,8 @@ |
|||
let pkg |
|||
let pkg; |
|||
try { |
|||
pkg = require('../package.json') |
|||
pkg = require("../package.json"); |
|||
} catch (err) { |
|||
pkg = require('../../package.json') |
|||
pkg = require("../../package.json"); |
|||
} |
|||
|
|||
module.exports = pkg |
|||
module.exports = pkg; |
|||
|
@ -1,48 +1,47 @@ |
|||
const ms = require('ms') |
|||
const ms = require("ms"); |
|||
|
|||
const Now = require('../lib') |
|||
const Now = require("../lib"); |
|||
|
|||
async function parsePlan(res) { |
|||
let id |
|||
let until |
|||
let id; |
|||
let until; |
|||
|
|||
const {subscription} = await res.json() |
|||
const { subscription } = await res.json(); |
|||
|
|||
if (subscription) { |
|||
id = subscription.plan.id |
|||
id = subscription.plan.id; |
|||
|
|||
if (subscription.cancel_at_period_end) { |
|||
until = ms( |
|||
new Date(subscription.current_period_end * 1000) - new Date(), |
|||
{long: true} |
|||
) |
|||
{ long: true } |
|||
); |
|||
} |
|||
} else { |
|||
id = 'oss' |
|||
id = "oss"; |
|||
} |
|||
|
|||
return {id, until} |
|||
return { id, until }; |
|||
} |
|||
|
|||
module.exports = class Plans extends Now { |
|||
|
|||
async getCurrent() { |
|||
const res = await this._fetch('/www/user/plan') |
|||
const res = await this._fetch("/www/user/plan"); |
|||
|
|||
return await parsePlan(res) |
|||
return await parsePlan(res); |
|||
} |
|||
|
|||
async set(plan) { |
|||
const res = await this._fetch('/www/user/plan', { |
|||
method: 'PUT', |
|||
body: {plan} |
|||
}) |
|||
const res = await this._fetch("/www/user/plan", { |
|||
method: "PUT", |
|||
body: { plan } |
|||
}); |
|||
|
|||
if (res.ok) { |
|||
return await parsePlan(res) |
|||
return await parsePlan(res); |
|||
} |
|||
const err = new Error(res.statusText) |
|||
err.res = res |
|||
throw err |
|||
const err = new Error(res.statusText); |
|||
err.res = res; |
|||
throw err; |
|||
} |
|||
} |
|||
}; |
|||
|
@ -1,80 +1,86 @@ |
|||
// Native
|
|||
const {join} = require('path') |
|||
const { join } = require("path"); |
|||
|
|||
// Packages
|
|||
const fs = require('fs-promise') |
|||
const chalk = require('chalk') |
|||
const fs = require("fs-promise"); |
|||
const chalk = require("chalk"); |
|||
|
|||
// Ours
|
|||
const {error} = require('./error') |
|||
const readMetaData = require('./read-metadata') |
|||
const NowAlias = require('./alias') |
|||
const { error } = require("./error"); |
|||
const readMetaData = require("./read-metadata"); |
|||
const NowAlias = require("./alias"); |
|||
|
|||
exports.assignAlias = async (autoAlias, token, deployment, apiUrl, debug) => { |
|||
const aliases = new NowAlias(apiUrl, token, {debug}) |
|||
console.log(`> Assigning alias ${chalk.bold.underline(autoAlias)} to deployment...`) |
|||
const aliases = new NowAlias(apiUrl, token, { debug }); |
|||
console.log( |
|||
`> Assigning alias ${chalk.bold.underline(autoAlias)} to deployment...` |
|||
); |
|||
|
|||
// Assign alias
|
|||
await aliases.set(String(deployment), String(autoAlias)) |
|||
} |
|||
await aliases.set(String(deployment), String(autoAlias)); |
|||
}; |
|||
|
|||
exports.reAlias = async (token, host, help, exit, apiUrl, debug, alias) => { |
|||
const path = process.cwd() |
|||
const path = process.cwd(); |
|||
|
|||
const configFiles = { |
|||
pkg: join(path, 'package.json'), |
|||
nowJSON: join(path, 'now.json') |
|||
} |
|||
pkg: join(path, "package.json"), |
|||
nowJSON: join(path, "now.json") |
|||
}; |
|||
|
|||
if (!fs.existsSync(configFiles.pkg) && !fs.existsSync(configFiles.nowJSON)) { |
|||
error(`Couldn't find a now.json or package.json file with an alias list in it`) |
|||
return |
|||
error( |
|||
`Couldn't find a now.json or package.json file with an alias list in it` |
|||
); |
|||
return; |
|||
} |
|||
|
|||
const {nowConfig, name} = await readMetaData(path, { |
|||
deploymentType: 'npm', // hard coding settings…
|
|||
const { nowConfig, name } = await readMetaData(path, { |
|||
deploymentType: "npm", // hard coding settings…
|
|||
quiet: true // `quiet`
|
|||
}) |
|||
}); |
|||
|
|||
if (!host) { |
|||
const lastAlias = await alias.last(name) |
|||
host = lastAlias.url |
|||
const lastAlias = await alias.last(name); |
|||
host = lastAlias.url; |
|||
} |
|||
|
|||
if (!nowConfig) { |
|||
help() |
|||
return exit(0) |
|||
help(); |
|||
return exit(0); |
|||
} |
|||
|
|||
let pointers = [] |
|||
let pointers = []; |
|||
|
|||
if (nowConfig.alias) { |
|||
const value = nowConfig.alias |
|||
const value = nowConfig.alias; |
|||
|
|||
if (typeof value === 'string') { |
|||
pointers.push(value) |
|||
if (typeof value === "string") { |
|||
pointers.push(value); |
|||
} else if (Array.isArray(value)) { |
|||
pointers = pointers.concat(nowConfig.alias) |
|||
pointers = pointers.concat(nowConfig.alias); |
|||
} else { |
|||
error(`Property ${chalk.grey('aliases')} is not a valid array or string`) |
|||
return exit(1) |
|||
error(`Property ${chalk.grey("aliases")} is not a valid array or string`); |
|||
return exit(1); |
|||
} |
|||
} |
|||
|
|||
if (nowConfig.aliases && Array.isArray(nowConfig.aliases)) { |
|||
console.log(`${chalk.red('Deprecated!')} The property ${chalk.grey('aliases')} will be ` + |
|||
`removed from the config file soon.`) |
|||
console.log('Read more about the new way here: http://bit.ly/2l2v5Fg\n') |
|||
console.log( |
|||
`${chalk.red("Deprecated!")} The property ${chalk.grey("aliases")} will be ` + |
|||
`removed from the config file soon.` |
|||
); |
|||
console.log("Read more about the new way here: http://bit.ly/2l2v5Fg\n"); |
|||
|
|||
pointers = pointers.concat(nowConfig.aliases) |
|||
pointers = pointers.concat(nowConfig.aliases); |
|||
} |
|||
|
|||
if (pointers.length === 0) { |
|||
help() |
|||
return exit(0) |
|||
help(); |
|||
return exit(0); |
|||
} |
|||
|
|||
for (const pointer of pointers) { |
|||
await exports.assignAlias(pointer, token, host, apiUrl, debug) |
|||
await exports.assignAlias(pointer, token, host, apiUrl, debug); |
|||
} |
|||
} |
|||
}; |
|||
|
@ -1,115 +1,117 @@ |
|||
// Ours
|
|||
const Now = require('../lib') |
|||
const Now = require("../lib"); |
|||
|
|||
module.exports = class Secrets extends Now { |
|||
ls() { |
|||
return this.listSecrets() |
|||
return this.listSecrets(); |
|||
} |
|||
|
|||
rm(nameOrId) { |
|||
return this.retry(async (bail, attempt) => { |
|||
if (this._debug) { |
|||
console.time(`> [debug] #${attempt} DELETE /secrets/${nameOrId}`) |
|||
console.time(`> [debug] #${attempt} DELETE /secrets/${nameOrId}`); |
|||
} |
|||
|
|||
const res = await this._fetch(`/now/secrets/${nameOrId}`, {method: 'DELETE'}) |
|||
const res = await this._fetch(`/now/secrets/${nameOrId}`, { |
|||
method: "DELETE" |
|||
}); |
|||
|
|||
if (this._debug) { |
|||
console.timeEnd(`> [debug] #${attempt} DELETE /secrets/${nameOrId}`) |
|||
console.timeEnd(`> [debug] #${attempt} DELETE /secrets/${nameOrId}`); |
|||
} |
|||
|
|||
if (res.status === 403) { |
|||
return bail(new Error('Unauthorized')) |
|||
return bail(new Error("Unauthorized")); |
|||
} |
|||
|
|||
const body = await res.json() |
|||
const body = await res.json(); |
|||
|
|||
if (res.status !== 200) { |
|||
if (res.status === 404 || res.status === 400) { |
|||
const err = new Error(body.error.message) |
|||
err.userError = true |
|||
return bail(err) |
|||
const err = new Error(body.error.message); |
|||
err.userError = true; |
|||
return bail(err); |
|||
} |
|||
|
|||
throw new Error(body.error.message) |
|||
throw new Error(body.error.message); |
|||
} |
|||
|
|||
return body |
|||
}) |
|||
return body; |
|||
}); |
|||
} |
|||
|
|||
add(name, value) { |
|||
return this.retry(async (bail, attempt) => { |
|||
if (this._debug) { |
|||
console.time(`> [debug] #${attempt} POST /secrets`) |
|||
console.time(`> [debug] #${attempt} POST /secrets`); |
|||
} |
|||
|
|||
const res = await this._fetch('/now/secrets', { |
|||
method: 'POST', |
|||
const res = await this._fetch("/now/secrets", { |
|||
method: "POST", |
|||
body: { |
|||
name, |
|||
value: value.toString() |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
if (this._debug) { |
|||
console.timeEnd(`> [debug] #${attempt} POST /secrets`) |
|||
console.timeEnd(`> [debug] #${attempt} POST /secrets`); |
|||
} |
|||
|
|||
if (res.status === 403) { |
|||
return bail(new Error('Unauthorized')) |
|||
return bail(new Error("Unauthorized")); |
|||
} |
|||
|
|||
const body = await res.json() |
|||
const body = await res.json(); |
|||
|
|||
if (res.status !== 200) { |
|||
if (res.status === 404 || res.status === 400) { |
|||
const err = new Error(body.error.message) |
|||
err.userError = true |
|||
return bail(err) |
|||
const err = new Error(body.error.message); |
|||
err.userError = true; |
|||
return bail(err); |
|||
} |
|||
|
|||
throw new Error(body.error.message) |
|||
throw new Error(body.error.message); |
|||
} |
|||
|
|||
return body |
|||
}) |
|||
return body; |
|||
}); |
|||
} |
|||
|
|||
rename(nameOrId, newName) { |
|||
return this.retry(async (bail, attempt) => { |
|||
if (this._debug) { |
|||
console.time(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`) |
|||
console.time(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`); |
|||
} |
|||
|
|||
const res = await this._fetch(`/now/secrets/${nameOrId}`, { |
|||
method: 'PATCH', |
|||
method: "PATCH", |
|||
body: { |
|||
name: newName |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
if (this._debug) { |
|||
console.timeEnd(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`) |
|||
console.timeEnd(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`); |
|||
} |
|||
|
|||
if (res.status === 403) { |
|||
return bail(new Error('Unauthorized')) |
|||
return bail(new Error("Unauthorized")); |
|||
} |
|||
|
|||
const body = await res.json() |
|||
const body = await res.json(); |
|||
|
|||
if (res.status !== 200) { |
|||
if (res.status === 404 || res.status === 400) { |
|||
const err = new Error(body.error.message) |
|||
err.userError = true |
|||
return bail(err) |
|||
const err = new Error(body.error.message); |
|||
err.userError = true; |
|||
return bail(err); |
|||
} |
|||
|
|||
throw new Error(body.error.message) |
|||
throw new Error(body.error.message); |
|||
} |
|||
|
|||
return body |
|||
}) |
|||
return body; |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
|
@ -1,5 +1,5 @@ |
|||
function strlen(str) { |
|||
return str.replace(/\x1b[^m]*m/g, '').length |
|||
return str.replace(/\x1b[^m]*m/g, "").length; |
|||
} |
|||
|
|||
module.exports = strlen |
|||
module.exports = strlen; |
|||
|
@ -1,22 +1,22 @@ |
|||
// Native
|
|||
const {resolve} = require('path') |
|||
const { resolve } = require("path"); |
|||
|
|||
// Ours
|
|||
const {npm: getFiles} = require('./get-files') |
|||
const { npm: getFiles } = require("./get-files"); |
|||
|
|||
getFiles(resolve('../mng-test/files-in-package')) |
|||
.then(files => { |
|||
console.log(files) |
|||
getFiles(resolve("../mng-test/files-in-package")) |
|||
.then(files => { |
|||
console.log(files); |
|||
|
|||
getFiles(resolve('../mng-test/files-in-package-ignore')) |
|||
.then(files2 => { |
|||
console.log('ignored: ') |
|||
console.log(files2) |
|||
getFiles(resolve("../mng-test/files-in-package-ignore")) |
|||
.then(files2 => { |
|||
console.log("ignored: "); |
|||
console.log(files2); |
|||
}) |
|||
.catch(err => { |
|||
console.log(err.stack); |
|||
}); |
|||
}) |
|||
.catch(err => { |
|||
console.log(err.stack) |
|||
}) |
|||
}) |
|||
.catch(err => { |
|||
console.log(err.stack) |
|||
}) |
|||
console.log(err.stack); |
|||
}); |
|||
|
@ -1,7 +1,7 @@ |
|||
// Native
|
|||
const os = require('os') |
|||
const os = require("os"); |
|||
|
|||
// Ours
|
|||
const {version} = require('./pkg') |
|||
const { version } = require("./pkg"); |
|||
|
|||
module.exports = `now ${version} node-${process.version} ${os.platform()} (${os.arch()})` |
|||
module.exports = `now ${version} node-${process.version} ${os.platform()} (${os.arch()})`; |
|||
|
@ -1,27 +1,33 @@ |
|||
// Packages
|
|||
const gMaps = require('@google/maps') |
|||
const gMaps = require("@google/maps"); |
|||
|
|||
const MAPS_API_KEY = 'AIzaSyALfKTQ6AiIoJ8WGDXR3E7IBOwlHoTPfYY' |
|||
const MAPS_API_KEY = "AIzaSyALfKTQ6AiIoJ8WGDXR3E7IBOwlHoTPfYY"; |
|||
|
|||
// eslint-disable-next-line camelcase
|
|||
module.exports = function ({country, zipCode: postal_code}) { |
|||
module.exports = function({ country, zipCode: postal_code }) { |
|||
return new Promise(resolve => { |
|||
const maps = gMaps.createClient({key: MAPS_API_KEY}) |
|||
maps.geocode({ |
|||
address: `${postal_code} ${country}` // eslint-disable-line camelcase
|
|||
}, (err, res) => { |
|||
if (err || res.json.results.length === 0) { |
|||
resolve() |
|||
} |
|||
const maps = gMaps.createClient({ key: MAPS_API_KEY }); |
|||
maps.geocode( |
|||
{ |
|||
address: `${postal_code} ${country}` // eslint-disable-line camelcase
|
|||
}, |
|||
(err, res) => { |
|||
if (err || res.json.results.length === 0) { |
|||
resolve(); |
|||
} |
|||
|
|||
const data = res.json.results[0] |
|||
const components = {} |
|||
data.address_components.forEach(c => { |
|||
components[c.types[0]] = c |
|||
}) |
|||
const state = components.administrative_area_level_1 |
|||
const city = components.locality |
|||
resolve({state: state && state.long_name, city: city && city.long_name}) |
|||
}) |
|||
}) |
|||
} |
|||
const data = res.json.results[0]; |
|||
const components = {}; |
|||
data.address_components.forEach(c => { |
|||
components[c.types[0]] = c; |
|||
}); |
|||
const state = components.administrative_area_level_1; |
|||
const city = components.locality; |
|||
resolve({ |
|||
state: state && state.long_name, |
|||
city: city && city.long_name |
|||
}); |
|||
} |
|||
); |
|||
}); |
|||
}; |
|||
|
@ -1,49 +1,49 @@ |
|||
// Native
|
|||
const os = require('os') |
|||
const path = require('path') |
|||
const os = require("os"); |
|||
const path = require("path"); |
|||
|
|||
const checkPath = async dir => { |
|||
if (!dir) { |
|||
return |
|||
return; |
|||
} |
|||
|
|||
const home = os.homedir() |
|||
let location |
|||
const home = os.homedir(); |
|||
let location; |
|||
|
|||
const paths = { |
|||
home, |
|||
desktop: path.join(home, 'Desktop'), |
|||
downloads: path.join(home, 'Downloads') |
|||
} |
|||
desktop: path.join(home, "Desktop"), |
|||
downloads: path.join(home, "Downloads") |
|||
}; |
|||
|
|||
for (const locationPath in paths) { |
|||
if (!{}.hasOwnProperty.call(paths, locationPath)) { |
|||
continue |
|||
continue; |
|||
} |
|||
|
|||
if (dir === paths[locationPath]) { |
|||
location = locationPath |
|||
location = locationPath; |
|||
} |
|||
} |
|||
|
|||
if (!location) { |
|||
return |
|||
return; |
|||
} |
|||
|
|||
let locationName |
|||
let locationName; |
|||
|
|||
switch (location) { |
|||
case 'home': |
|||
locationName = 'user directory' |
|||
break |
|||
case 'downloads': |
|||
locationName = 'downloads directory' |
|||
break |
|||
case "home": |
|||
locationName = "user directory"; |
|||
break; |
|||
case "downloads": |
|||
locationName = "downloads directory"; |
|||
break; |
|||
default: |
|||
locationName = location |
|||
locationName = location; |
|||
} |
|||
|
|||
throw new Error(`You're trying to deploy your ${locationName}.`) |
|||
} |
|||
throw new Error(`You're trying to deploy your ${locationName}.`); |
|||
}; |
|||
|
|||
module.exports = checkPath |
|||
module.exports = checkPath; |
|||
|
@ -1,86 +1,92 @@ |
|||
const chalk = require('chalk') |
|||
const inquirer = require('inquirer') |
|||
const stripAnsi = require('strip-ansi') |
|||
const chalk = require("chalk"); |
|||
const inquirer = require("inquirer"); |
|||
const stripAnsi = require("strip-ansi"); |
|||
|
|||
/* eslint-disable no-multiple-empty-lines, no-var, no-undef, no-eq-null, eqeqeq, semi */ |
|||
inquirer.prompt.prompts.list.prototype.getQuestion = function () { |
|||
var message = chalk.bold('> ' + this.opt.message) + ' ' |
|||
inquirer.prompt.prompts.list.prototype.getQuestion = function() { |
|||
var message = chalk.bold("> " + this.opt.message) + " "; |
|||
|
|||
// Append the default if available, and if question isn't answered
|
|||
if (this.opt.default != null && this.status !== 'answered') { |
|||
message += chalk.dim('(' + this.opt.default + ') ') |
|||
if (this.opt.default != null && this.status !== "answered") { |
|||
message += chalk.dim("(" + this.opt.default + ") "); |
|||
} |
|||
|
|||
return message |
|||
return message; |
|||
}; |
|||
/* eslint-enable */ |
|||
|
|||
function getLength(string) { |
|||
let biggestLength = 0 |
|||
string.split('\n').map(str => { |
|||
str = stripAnsi(str) |
|||
let biggestLength = 0; |
|||
string.split("\n").map(str => { |
|||
str = stripAnsi(str); |
|||
if (str.length > biggestLength) { |
|||
biggestLength = str.length |
|||
biggestLength = str.length; |
|||
} |
|||
return undefined |
|||
}) |
|||
return biggestLength |
|||
return undefined; |
|||
}); |
|||
return biggestLength; |
|||
} |
|||
|
|||
module.exports = async function ({ |
|||
message = 'the question', |
|||
choices = [{ // eslint-disable-line no-unused-vars
|
|||
name: 'something\ndescription\ndetails\netc', |
|||
value: 'something unique', |
|||
short: 'generally the first line of `name`' |
|||
}], |
|||
pageSize = 15, // Show 15 lines without scrolling (~4 credit cards)
|
|||
separator = true, // puts a blank separator between each choice
|
|||
abort = 'end' // wether the `abort` option will be at the `start` or the `end`
|
|||
}) { |
|||
let biggestLength = 0 |
|||
module.exports = async function( |
|||
{ |
|||
message = "the question", |
|||
// eslint-disable-line no-unused-vars
|
|||
choices = [ |
|||
{ |
|||
name: "something\ndescription\ndetails\netc", |
|||
value: "something unique", |
|||
short: "generally the first line of `name`" |
|||
} |
|||
], |
|||
pageSize = 15, // Show 15 lines without scrolling (~4 credit cards)
|
|||
separator = true, // puts a blank separator between each choice
|
|||
abort = "end" // wether the `abort` option will be at the `start` or the `end`
|
|||
} |
|||
) { |
|||
let biggestLength = 0; |
|||
|
|||
choices = choices.map(choice => { |
|||
if (choice.name) { |
|||
const length = getLength(choice.name) |
|||
const length = getLength(choice.name); |
|||
if (length > biggestLength) { |
|||
biggestLength = length |
|||
biggestLength = length; |
|||
} |
|||
return choice |
|||
return choice; |
|||
} |
|||
throw new Error('Invalid choice') |
|||
}) |
|||
throw new Error("Invalid choice"); |
|||
}); |
|||
|
|||
if (separator === true) { |
|||
choices = choices.reduce((prev, curr) => ( |
|||
prev.concat(new inquirer.Separator(' '), curr) |
|||
), []) |
|||
choices = choices.reduce( |
|||
(prev, curr) => prev.concat(new inquirer.Separator(" "), curr), |
|||
[] |
|||
); |
|||
} |
|||
|
|||
const abortSeparator = new inquirer.Separator('─'.repeat(biggestLength)) |
|||
const abortSeparator = new inquirer.Separator("─".repeat(biggestLength)); |
|||
const _abort = { |
|||
name: 'Abort', |
|||
name: "Abort", |
|||
value: undefined |
|||
} |
|||
}; |
|||
|
|||
if (abort === 'start') { |
|||
const blankSep = choices.shift() |
|||
choices.unshift(abortSeparator) |
|||
choices.unshift(_abort) |
|||
choices.unshift(blankSep) |
|||
if (abort === "start") { |
|||
const blankSep = choices.shift(); |
|||
choices.unshift(abortSeparator); |
|||
choices.unshift(_abort); |
|||
choices.unshift(blankSep); |
|||
} else { |
|||
choices.push(abortSeparator) |
|||
choices.push(_abort) |
|||
choices.push(abortSeparator); |
|||
choices.push(_abort); |
|||
} |
|||
|
|||
const nonce = Date.now() |
|||
const nonce = Date.now(); |
|||
const answer = await inquirer.prompt({ |
|||
name: nonce, |
|||
type: 'list', |
|||
type: "list", |
|||
message, |
|||
choices, |
|||
pageSize |
|||
}) |
|||
}); |
|||
|
|||
return answer[nonce] |
|||
} |
|||
return answer[nonce]; |
|||
}; |
|||
|
@ -1,56 +1,59 @@ |
|||
const chalk = require('chalk') |
|||
const chalk = require("chalk"); |
|||
|
|||
module.exports = (label, { |
|||
defaultValue = false, |
|||
abortSequences = new Set(['\x03']), |
|||
resolveChars = new Set(['\r']), |
|||
yesChar = 'y', |
|||
noChar = 'n', |
|||
stdin = process.stdin, |
|||
stdout = process.stdout, |
|||
trailing = '\n' |
|||
} = {}) => { |
|||
module.exports = ( |
|||
label, |
|||
{ |
|||
defaultValue = false, |
|||
abortSequences = new Set(["\x03"]), |
|||
resolveChars = new Set(["\r"]), |
|||
yesChar = "y", |
|||
noChar = "n", |
|||
stdin = process.stdin, |
|||
stdout = process.stdout, |
|||
trailing = "\n" |
|||
} = {} |
|||
) => { |
|||
return new Promise((resolve, reject) => { |
|||
const isRaw = process.stdin.isRaw |
|||
const isRaw = process.stdin.isRaw; |
|||
|
|||
stdin.setRawMode(true) |
|||
stdin.resume() |
|||
stdin.setRawMode(true); |
|||
stdin.resume(); |
|||
|
|||
function restore() { |
|||
console.log(trailing) |
|||
stdin.setRawMode(isRaw) |
|||
stdin.pause() |
|||
stdin.removeListener('data', onData) |
|||
console.log(trailing); |
|||
stdin.setRawMode(isRaw); |
|||
stdin.pause(); |
|||
stdin.removeListener("data", onData); |
|||
} |
|||
|
|||
function onData(buffer) { |
|||
const data = buffer.toString() |
|||
const data = buffer.toString(); |
|||
|
|||
if (abortSequences.has(data)) { |
|||
restore() |
|||
return reject(new Error('USER_ABORT')) |
|||
restore(); |
|||
return reject(new Error("USER_ABORT")); |
|||
} |
|||
|
|||
if (resolveChars.has(data[0])) { |
|||
restore() |
|||
resolve(defaultValue) |
|||
restore(); |
|||
resolve(defaultValue); |
|||
} else if (data[0].toLowerCase() === yesChar) { |
|||
restore() |
|||
resolve(true) |
|||
restore(); |
|||
resolve(true); |
|||
} else if (data[0].toLowerCase() === noChar) { |
|||
restore() |
|||
resolve(false) |
|||
restore(); |
|||
resolve(false); |
|||
} else { |
|||
// ignore extraneous input
|
|||
} |
|||
} |
|||
|
|||
const defaultText = defaultValue === null ? |
|||
`[${yesChar}|${noChar}]` : |
|||
defaultValue ? |
|||
`[${chalk.bold(yesChar.toUpperCase())}|${noChar}]` : |
|||
`[${yesChar}|${chalk.bold(noChar.toUpperCase())}]` |
|||
stdout.write(`${chalk.gray('-')} ${label} ${chalk.gray(defaultText)} `) |
|||
stdin.on('data', onData) |
|||
}) |
|||
} |
|||
const defaultText = defaultValue === null |
|||
? `[${yesChar}|${noChar}]` |
|||
: defaultValue |
|||
? `[${chalk.bold(yesChar.toUpperCase())}|${noChar}]` |
|||
: `[${yesChar}|${chalk.bold(noChar.toUpperCase())}]`; |
|||
stdout.write(`${chalk.gray("-")} ${label} ${chalk.gray(defaultText)} `); |
|||
stdin.on("data", onData); |
|||
}); |
|||
}; |
|||
|
@ -1,207 +1,223 @@ |
|||
const ansiEscapes = require('ansi-escapes') |
|||
const ansiRegex = require('ansi-regex') |
|||
const chalk = require('chalk') |
|||
const stripAnsi = require('strip-ansi') |
|||
const ansiEscapes = require("ansi-escapes"); |
|||
const ansiRegex = require("ansi-regex"); |
|||
const chalk = require("chalk"); |
|||
const stripAnsi = require("strip-ansi"); |
|||
|
|||
const ESCAPES = { |
|||
LEFT: '\x1b[D', |
|||
RIGHT: '\x1b[C', |
|||
CTRL_C: '\x03', |
|||
BACKSPACE: '\x08', |
|||
CTRL_H: '\x7f', |
|||
CARRIAGE: '\r' |
|||
} |
|||
LEFT: "\x1b[D", |
|||
RIGHT: "\x1b[C", |
|||
CTRL_C: "\x03", |
|||
BACKSPACE: "\x08", |
|||
CTRL_H: "\x7f", |
|||
CARRIAGE: "\r" |
|||
}; |
|||
|
|||
module.exports = function ({ |
|||
label = '', |
|||
initialValue = '', |
|||
// can be:
|
|||
// - `false`, which does nothing
|
|||
// - `cc`, for credit cards
|
|||
// - `date`, for dates in the mm / yyyy format
|
|||
mask = false, |
|||
placeholder = '', |
|||
abortSequences = new Set(['\x03']), |
|||
eraseSequences = new Set([ESCAPES.BACKSPACE, ESCAPES.CTRL_H]), |
|||
resolveChars = new Set([ESCAPES.CARRIAGE]), |
|||
stdin = process.stdin, |
|||
stdout = process.stdout, |
|||
// char to print before resolving/rejecting the promise
|
|||
// if `false`, nothing will be printed
|
|||
trailing = ansiEscapes.eraseLines(1), |
|||
// gets called on each keypress;
|
|||
// `data` contains the current keypress;
|
|||
// `futureValue` contains the current value + the
|
|||
// keypress in the correct place
|
|||
validateKeypress = (data, futureValue) => true, // eslint-disable-line no-unused-vars
|
|||
// get's called before the promise is resolved
|
|||
// returning `false` here will prevent the user from submiting the value
|
|||
validateValue = data => true, // eslint-disable-line no-unused-vars
|
|||
// receives the value of the input and should return a string
|
|||
// or false if no autocomplion is available
|
|||
autoComplete = value => false, // eslint-disable-line no-unused-vars
|
|||
autoCompleteChars = new Set([ |
|||
'\t', // tab
|
|||
'\x1b[C' // right arrow
|
|||
]) |
|||
} = {}) { |
|||
module.exports = function( |
|||
{ |
|||
label = "", |
|||
initialValue = "", |
|||
// can be:
|
|||
// - `false`, which does nothing
|
|||
// - `cc`, for credit cards
|
|||
// - `date`, for dates in the mm / yyyy format
|
|||
mask = false, |
|||
placeholder = "", |
|||
abortSequences = new Set(["\x03"]), |
|||
eraseSequences = new Set([ESCAPES.BACKSPACE, ESCAPES.CTRL_H]), |
|||
resolveChars = new Set([ESCAPES.CARRIAGE]), |
|||
stdin = process.stdin, |
|||
stdout = process.stdout, |
|||
// char to print before resolving/rejecting the promise
|
|||
// if `false`, nothing will be printed
|
|||
trailing = ansiEscapes.eraseLines(1), |
|||
// gets called on each keypress;
|
|||
// `data` contains the current keypress;
|
|||
// `futureValue` contains the current value + the
|
|||
// keypress in the correct place
|
|||
validateKeypress = (data, futureValue) => true, // eslint-disable-line no-unused-vars
|
|||
// get's called before the promise is resolved
|
|||
// returning `false` here will prevent the user from submiting the value
|
|||
validateValue = data => true, // eslint-disable-line no-unused-vars
|
|||
// receives the value of the input and should return a string
|
|||
// or false if no autocomplion is available
|
|||
autoComplete = value => false, // eslint-disable-line no-unused-vars
|
|||
// tab
|
|||
// right arrow
|
|||
autoCompleteChars = new Set(["\t", "\x1b[C"]) |
|||
} = {} |
|||
) { |
|||
return new Promise((resolve, reject) => { |
|||
const isRaw = process.stdin.isRaw |
|||
const isRaw = process.stdin.isRaw; |
|||
|
|||
let value |
|||
let caretOffset = 0 |
|||
let regex |
|||
let suggestion = '' |
|||
let value; |
|||
let caretOffset = 0; |
|||
let regex; |
|||
let suggestion = ""; |
|||
|
|||
stdout.write(label) |
|||
value = initialValue |
|||
stdout.write(initialValue) |
|||
stdout.write(label); |
|||
value = initialValue; |
|||
stdout.write(initialValue); |
|||
if (mask) { |
|||
if (!value) { |
|||
value = chalk.gray(placeholder) |
|||
caretOffset = 0 - stripAnsi(value).length |
|||
stdout.write(value) |
|||
stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset))) |
|||
value = chalk.gray(placeholder); |
|||
caretOffset = 0 - stripAnsi(value).length; |
|||
stdout.write(value); |
|||
stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset))); |
|||
} |
|||
|
|||
regex = placeholder.split('').reduce((prev, curr) => { |
|||
if (curr !== ' ' && !prev.includes(curr)) { |
|||
if (curr === '/') { |
|||
prev.push(' / ') |
|||
} else { |
|||
prev.push(curr) |
|||
} |
|||
} |
|||
return prev |
|||
}, []).join('|') |
|||
regex = new RegExp(`(${regex})`, 'g') |
|||
regex = placeholder |
|||
.split("") |
|||
.reduce( |
|||
(prev, curr) => { |
|||
if (curr !== " " && !prev.includes(curr)) { |
|||
if (curr === "/") { |
|||
prev.push(" / "); |
|||
} else { |
|||
prev.push(curr); |
|||
} |
|||
} |
|||
return prev; |
|||
}, |
|||
[] |
|||
) |
|||
.join("|"); |
|||
regex = new RegExp(`(${regex})`, "g"); |
|||
} |
|||
stdin.setRawMode(true) |
|||
stdin.resume() |
|||
stdin.setRawMode(true); |
|||
stdin.resume(); |
|||
|
|||
function restore() { |
|||
stdin.setRawMode(isRaw) |
|||
stdin.pause() |
|||
stdin.removeListener('data', onData) |
|||
stdin.setRawMode(isRaw); |
|||
stdin.pause(); |
|||
stdin.removeListener("data", onData); |
|||
if (trailing) { |
|||
stdout.write(trailing) |
|||
stdout.write(trailing); |
|||
} |
|||
} |
|||
|
|||
async function onData(buffer) { |
|||
const data = buffer.toString() |
|||
value = stripAnsi(value) |
|||
const data = buffer.toString(); |
|||
value = stripAnsi(value); |
|||
|
|||
if (abortSequences.has(data)) { |
|||
restore() |
|||
return reject(new Error('USER_ABORT')) |
|||
restore(); |
|||
return reject(new Error("USER_ABORT")); |
|||
} |
|||
|
|||
if (suggestion !== '' && !caretOffset && autoCompleteChars.has(data)) { |
|||
value += stripAnsi(suggestion) |
|||
suggestion = '' |
|||
if (suggestion !== "" && !caretOffset && autoCompleteChars.has(data)) { |
|||
value += stripAnsi(suggestion); |
|||
suggestion = ""; |
|||
} else if (data === ESCAPES.LEFT) { |
|||
if (value.length > Math.abs(caretOffset)) { |
|||
caretOffset-- |
|||
caretOffset--; |
|||
} |
|||
} else if (data === ESCAPES.RIGHT) { |
|||
if (caretOffset < 0) { |
|||
caretOffset++ |
|||
caretOffset++; |
|||
} |
|||
} else if (eraseSequences.has(data)) { |
|||
let char |
|||
if ((mask) && value.length > Math.abs(caretOffset)) { |
|||
if (value[value.length + caretOffset - 1] === ' ') { |
|||
if (value[value.length + caretOffset - 2] === '/') { |
|||
caretOffset -= 1 |
|||
let char; |
|||
if (mask && value.length > Math.abs(caretOffset)) { |
|||
if (value[value.length + caretOffset - 1] === " ") { |
|||
if (value[value.length + caretOffset - 2] === "/") { |
|||
caretOffset -= 1; |
|||
} |
|||
char = placeholder[value.length + caretOffset] |
|||
value = value.substr(0, value.length + caretOffset - 2) + char + |
|||
value.substr(value.length + caretOffset - 1) |
|||
caretOffset-- |
|||
char = placeholder[value.length + caretOffset]; |
|||
value = value.substr(0, value.length + caretOffset - 2) + |
|||
char + |
|||
value.substr(value.length + caretOffset - 1); |
|||
caretOffset--; |
|||
} else { |
|||
char = placeholder[value.length + caretOffset - 1] |
|||
value = value.substr(0, value.length + caretOffset - 1) + char + |
|||
value.substr(value.length + caretOffset) |
|||
char = placeholder[value.length + caretOffset - 1]; |
|||
value = value.substr(0, value.length + caretOffset - 1) + |
|||
char + |
|||
value.substr(value.length + caretOffset); |
|||
} |
|||
caretOffset-- |
|||
caretOffset--; |
|||
} else { |
|||
value = value.substr(0, value.length + caretOffset - 1) + |
|||
value.substr(value.length + caretOffset) |
|||
value.substr(value.length + caretOffset); |
|||
} |
|||
suggestion = '' |
|||
suggestion = ""; |
|||
} else if (resolveChars.has(data)) { |
|||
if (validateValue(value)) { |
|||
restore() |
|||
resolve(value) |
|||
restore(); |
|||
resolve(value); |
|||
} else { |
|||
if (mask === 'cc' || mask === 'ccv') { |
|||
value = value.replace(/\s/g, '').replace(/(.{4})/g, '$1 ').trim() |
|||
value = value.replace(regex, chalk.gray('$1')) |
|||
} else if (mask === 'expDate') { |
|||
value = value.replace(regex, chalk.gray('$1')) |
|||
if (mask === "cc" || mask === "ccv") { |
|||
value = value.replace(/\s/g, "").replace(/(.{4})/g, "$1 ").trim(); |
|||
value = value.replace(regex, chalk.gray("$1")); |
|||
} else if (mask === "expDate") { |
|||
value = value.replace(regex, chalk.gray("$1")); |
|||
} |
|||
const l = chalk.red(label.replace('-', '✖')) |
|||
stdout.write(ansiEscapes.eraseLines(1) + l + value + ansiEscapes.beep) |
|||
const l = chalk.red(label.replace("-", "✖")); |
|||
stdout.write( |
|||
ansiEscapes.eraseLines(1) + l + value + ansiEscapes.beep |
|||
); |
|||
if (caretOffset) { |
|||
process.stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset))) |
|||
process.stdout.write( |
|||
ansiEscapes.cursorBackward(Math.abs(caretOffset)) |
|||
); |
|||
} |
|||
} |
|||
return |
|||
return; |
|||
} else if (!ansiRegex().test(data)) { |
|||
let tmp = value.substr(0, value.length + caretOffset) + data + |
|||
value.substr(value.length + caretOffset) |
|||
let tmp = value.substr(0, value.length + caretOffset) + |
|||
data + |
|||
value.substr(value.length + caretOffset); |
|||
|
|||
if (mask) { |
|||
if (/\d/.test(data) && caretOffset !== 0) { |
|||
if (value[value.length + caretOffset + 1] === ' ') { |
|||
tmp = value.substr(0, value.length + caretOffset) + data + |
|||
value.substr(value.length + caretOffset + 1) |
|||
caretOffset += 2 |
|||
if (value[value.length + caretOffset] === '/') { |
|||
caretOffset += 2 |
|||
if (value[value.length + caretOffset + 1] === " ") { |
|||
tmp = value.substr(0, value.length + caretOffset) + |
|||
data + |
|||
value.substr(value.length + caretOffset + 1); |
|||
caretOffset += 2; |
|||
if (value[value.length + caretOffset] === "/") { |
|||
caretOffset += 2; |
|||
} |
|||
} else { |
|||
tmp = value.substr(0, value.length + caretOffset) + data + |
|||
value.substr(value.length + caretOffset + 1) |
|||
caretOffset++ |
|||
tmp = value.substr(0, value.length + caretOffset) + |
|||
data + |
|||
value.substr(value.length + caretOffset + 1); |
|||
caretOffset++; |
|||
} |
|||
} else if (/\s/.test(data) && caretOffset < 0) { |
|||
caretOffset++ |
|||
tmp = value |
|||
caretOffset++; |
|||
tmp = value; |
|||
} else { |
|||
return stdout.write(ansiEscapes.beep) |
|||
return stdout.write(ansiEscapes.beep); |
|||
} |
|||
value = tmp |
|||
value = tmp; |
|||
} else if (validateKeypress(data, value)) { |
|||
value = tmp |
|||
value = tmp; |
|||
if (caretOffset === 0) { |
|||
const completion = await autoComplete(value) |
|||
const completion = await autoComplete(value); |
|||
if (completion) { |
|||
suggestion = chalk.gray(completion) |
|||
suggestion += ansiEscapes.cursorBackward(completion.length) |
|||
suggestion = chalk.gray(completion); |
|||
suggestion += ansiEscapes.cursorBackward(completion.length); |
|||
} else { |
|||
suggestion = '' |
|||
suggestion = ""; |
|||
} |
|||
} |
|||
} else { |
|||
return stdout.write(ansiEscapes.beep) |
|||
return stdout.write(ansiEscapes.beep); |
|||
} |
|||
} |
|||
|
|||
if (mask === 'cc' || mask === 'ccv') { |
|||
value = value.replace(/\s/g, '').replace(/(.{4})/g, '$1 ').trim() |
|||
value = value.replace(regex, chalk.gray('$1')) |
|||
} else if (mask === 'expDate') { |
|||
value = value.replace(regex, chalk.gray('$1')) |
|||
if (mask === "cc" || mask === "ccv") { |
|||
value = value.replace(/\s/g, "").replace(/(.{4})/g, "$1 ").trim(); |
|||
value = value.replace(regex, chalk.gray("$1")); |
|||
} else if (mask === "expDate") { |
|||
value = value.replace(regex, chalk.gray("$1")); |
|||
} |
|||
|
|||
stdout.write(ansiEscapes.eraseLines(1) + label + value + suggestion) |
|||
stdout.write(ansiEscapes.eraseLines(1) + label + value + suggestion); |
|||
if (caretOffset) { |
|||
process.stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset))) |
|||
process.stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset))); |
|||
} |
|||
} |
|||
|
|||
stdin.on('data', onData) |
|||
}) |
|||
} |
|||
stdin.on("data", onData); |
|||
}); |
|||
}; |
|||
|
@ -1,8 +1,7 @@ |
|||
const chalk = require('chalk') |
|||
const chalk = require("chalk"); |
|||
|
|||
// the equivalent of <code>, for embedding a cmd
|
|||
// eg: Please run ${cmd(woot)}
|
|||
|
|||
module.exports = cmd => ( |
|||
`${chalk.gray('`')}${chalk.cyan(cmd)}${chalk.gray('`')}` |
|||
) |
|||
module.exports = cmd => |
|||
`${chalk.gray("`")}${chalk.cyan(cmd)}${chalk.gray("`")}`; |
|||
|
@ -1,8 +1,7 @@ |
|||
const chalk = require('chalk') |
|||
const chalk = require("chalk"); |
|||
|
|||
// the equivalent of <code>, for embedding anything
|
|||
// you may want to take a look at ./cmd.js
|
|||
|
|||
module.exports = cmd => ( |
|||
`${chalk.gray('`')}${chalk.bold(cmd)}${chalk.gray('`')}` |
|||
) |
|||
module.exports = cmd => |
|||
`${chalk.gray("`")}${chalk.bold(cmd)}${chalk.gray("`")}`; |
|||
|
@ -1,7 +1,6 @@ |
|||
const chalk = require('chalk') |
|||
const chalk = require("chalk"); |
|||
|
|||
// prints an error message
|
|||
module.exports = msg => { |
|||
console.error(`${chalk.red('> Error!')} ${msg}`) |
|||
} |
|||
|
|||
console.error(`${chalk.red("> Error!")} ${msg}`); |
|||
}; |
|||
|
@ -1,7 +1,6 @@ |
|||
const chalk = require('chalk') |
|||
const chalk = require("chalk"); |
|||
|
|||
// prints an informational message
|
|||
module.exports = msg => { |
|||
console.log(`${chalk.gray('>')} ${msg}`) |
|||
} |
|||
|
|||
console.log(`${chalk.gray(">")} ${msg}`); |
|||
}; |
|||
|
@ -1 +1 @@ |
|||
module.exports = process.platform === 'win32' ? 'Δ' : '𝚫' |
|||
module.exports = process.platform === "win32" ? "Δ" : "𝚫"; |
|||
|
@ -1,8 +1,7 @@ |
|||
const chalk = require('chalk') |
|||
const chalk = require("chalk"); |
|||
|
|||
// returns a user param in a nice formatting
|
|||
// e.g.: google.com -> "google.com" (in bold)
|
|||
|
|||
module.exports = param => ( |
|||
chalk.bold(`${chalk.gray('"')}${chalk.bold(param)}${chalk.gray('"')}`) |
|||
) |
|||
module.exports = param => |
|||
chalk.bold(`${chalk.gray('"')}${chalk.bold(param)}${chalk.gray('"')}`); |
|||
|
@ -1,12 +1,10 @@ |
|||
const ms = require('ms') |
|||
const chalk = require('chalk') |
|||
const ms = require("ms"); |
|||
const chalk = require("chalk"); |
|||
|
|||
// returns a time delta with the right color
|
|||
// example: `[103ms]`
|
|||
|
|||
module.exports = () => { |
|||
const start = new Date() |
|||
return () => ( |
|||
chalk.gray(`[${ms(new Date() - start)}]`) |
|||
) |
|||
} |
|||
const start = new Date(); |
|||
return () => chalk.gray(`[${ms(new Date() - start)}]`); |
|||
}; |
|||
|
@ -1,6 +1,6 @@ |
|||
const chalk = require('chalk') |
|||
const chalk = require("chalk"); |
|||
|
|||
// prints a success message
|
|||
module.exports = msg => { |
|||
console.log(`${chalk.cyan('> Success!')} ${msg}`) |
|||
} |
|||
console.log(`${chalk.cyan("> Success!")} ${msg}`); |
|||
}; |
|||
|
@ -1,7 +1,5 @@ |
|||
const chalk = require('chalk') |
|||
const chalk = require("chalk"); |
|||
|
|||
// used for including uids in the output
|
|||
// example: `(dom_ji13dj2fih4fi2hf)`
|
|||
module.exports = id => ( |
|||
chalk.gray(`(${id})`) |
|||
) |
|||
module.exports = id => chalk.gray(`(${id})`); |
|||
|
@ -1,15 +1,15 @@ |
|||
const ora = require('ora') |
|||
const chalk = require('chalk') |
|||
const {eraseLine} = require('ansi-escapes') |
|||
const ora = require("ora"); |
|||
const chalk = require("chalk"); |
|||
const { eraseLine } = require("ansi-escapes"); |
|||
|
|||
// prints a spinner followed by the given text
|
|||
module.exports = msg => { |
|||
const spinner = ora(chalk.gray(msg)) |
|||
spinner.color = 'gray' |
|||
spinner.start() |
|||
const spinner = ora(chalk.gray(msg)); |
|||
spinner.color = "gray"; |
|||
spinner.start(); |
|||
|
|||
return () => { |
|||
spinner.stop() |
|||
process.stdout.write(eraseLine) |
|||
} |
|||
} |
|||
spinner.stop(); |
|||
process.stdout.write(eraseLine); |
|||
}; |
|||
}; |
|||
|
@ -1,35 +1,35 @@ |
|||
// Packages
|
|||
const chalk = require('chalk') |
|||
const chalk = require("chalk"); |
|||
|
|||
module.exports = function (opts) { |
|||
module.exports = function(opts) { |
|||
return new Promise((resolve, reject) => { |
|||
opts.forEach(([, text], i) => { |
|||
console.log(`${chalk.gray('>')} [${chalk.bold(i + 1)}] ${text}`) |
|||
}) |
|||
console.log(`${chalk.gray(">")} [${chalk.bold(i + 1)}] ${text}`); |
|||
}); |
|||
|
|||
const ondata = v => { |
|||
const s = v.toString() |
|||
const s = v.toString(); |
|||
|
|||
const cleanup = () => { |
|||
process.stdin.setRawMode(false) |
|||
process.stdin.removeListener('data', ondata) |
|||
} |
|||
process.stdin.setRawMode(false); |
|||
process.stdin.removeListener("data", ondata); |
|||
}; |
|||
|
|||
if (s === '\u0003') { |
|||
cleanup() |
|||
reject(new Error('Aborted')) |
|||
return |
|||
if (s === "\u0003") { |
|||
cleanup(); |
|||
reject(new Error("Aborted")); |
|||
return; |
|||
} |
|||
|
|||
const n = Number(s) |
|||
const n = Number(s); |
|||
if (opts[n - 1]) { |
|||
cleanup() |
|||
resolve(opts[n - 1][0]) |
|||
cleanup(); |
|||
resolve(opts[n - 1][0]); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
process.stdin.setRawMode(true) |
|||
process.stdin.resume() |
|||
process.stdin.on('data', ondata) |
|||
}) |
|||
} |
|||
process.stdin.setRawMode(true); |
|||
process.stdin.resume(); |
|||
process.stdin.on("data", ondata); |
|||
}); |
|||
}; |
|||
|
Loading…
Reference in new issue