Browse Source

Fixed missing token handling (#690)

* handle API errors when fetching user

* handle API errors in some of the core Now methods

* add ability for `handleError` to report full stack trace in debug mode

* make `responseError` utility reusable

* handle top-level promise errors in entry functions of all commands

* allow for logging in when user has a bad token

* fix CI

* Fix reference error

* Improve message

* Fix typo

* Prettify
master
Guillermo Rauch 8 years ago
committed by Leo Lamprecht
parent
commit
301166831a
  1. 9
      bin/now-alias.js
  2. 9
      bin/now-billing.js
  3. 7
      bin/now-certs.js
  4. 25
      bin/now-deploy.js
  5. 7
      bin/now-dns.js
  6. 9
      bin/now-domains.js
  7. 9
      bin/now-list.js
  8. 6
      bin/now-logout.js
  9. 2
      bin/now-logs.js
  10. 9
      bin/now-remove.js
  11. 7
      bin/now-scale.js
  12. 7
      bin/now-secrets.js
  13. 8
      bin/now-teams.js
  14. 8
      bin/now-upgrade.js
  15. 6
      bin/now-whoami.js
  16. 46
      lib/error.js
  17. 72
      lib/index.js
  18. 24
      lib/user.js

9
bin/now-alias.js

@ -15,7 +15,7 @@ const NowAlias = require('../lib/alias')
const NowDomains = require('../lib/domains')
const login = require('../lib/login')
const cfg = require('../lib/cfg')
const { error } = require('../lib/error')
const { handleError, error } = require('../lib/error')
const toHost = require('../lib/to-host')
const { reAlias } = require('../lib/re-alias')
const exit = require('../lib/utils/exit')
@ -136,7 +136,8 @@ if (argv.help) {
help()
exit(0)
} else {
Promise.resolve().then(async () => {
Promise.resolve()
.then(async () => {
const config = await cfg.read({ token: argv.token })
let token
@ -158,6 +159,10 @@ if (argv.help) {
exit(1)
}
})
.catch(err => {
handleError(err)
process.exit(1)
})
}
async function run({ token, config: { currentTeam, user } }) {

9
bin/now-billing.js

@ -11,7 +11,7 @@ const ms = require('ms')
// Ours
const login = require('../lib/login')
const cfg = require('../lib/cfg')
const { error } = require('../lib/error')
const { handleError, error } = require('../lib/error')
const NowCreditCards = require('../lib/credit-cards')
const indent = require('../lib/indent')
const listInput = require('../lib/utils/input/list')
@ -92,7 +92,8 @@ if (argv.help || !subcommand) {
help()
exit(0)
} else {
Promise.resolve().then(async () => {
Promise.resolve()
.then(async () => {
const config = await cfg.read({ token: argv.token })
let token
@ -113,6 +114,10 @@ if (argv.help || !subcommand) {
exit(1)
}
})
.catch(err => {
handleError(err)
process.exit(1)
})
}
// Builds a `choices` object that can be passesd to inquirer.prompt()

7
bin/now-certs.js

@ -92,7 +92,8 @@ if (argv.help || !subcommand) {
help()
exit(0)
} else {
Promise.resolve().then(async () => {
Promise.resolve()
.then(async () => {
const config = await cfg.read({ token: argv.token })
let token
@ -110,6 +111,10 @@ if (argv.help || !subcommand) {
exit(1)
}
})
.catch(err => {
handleError(err)
process.exit(1)
})
}
function formatExpirationDate(date) {

25
bin/now-deploy.js

@ -263,9 +263,6 @@ if (deploymentName || wantsPublic) {
let alwaysForwardNpm
async function main() {
let config = await cfg.read({ token: argv.token })
alwaysForwardNpm = config.forwardNpm
if (argv.h || argv.help) {
help()
return exit(0)
@ -274,6 +271,21 @@ async function main() {
return exit(0)
}
let config
try {
config = await cfg.read({ token: argv.token })
} catch (err) {
if (shouldLogin && err.userError) {
// We ignore user errors here, which means for example
// the token is mis-configured or revoked, if the user
// is attempting to log in again
config = {}
} else {
throw err
}
}
let token = argv.token || config.token
if (!token || shouldLogin) {
try {
@ -294,6 +306,8 @@ async function main() {
return exit(0)
}
alwaysForwardNpm = config.forwardNpm
// If we got to here then `token` should be set
try {
await sync({ token, config })
@ -868,4 +882,7 @@ function printLogs(host, token, currentTeam, user) {
})
}
main()
main().catch(err => {
handleError(err, { debug })
process.exit(1)
})

7
bin/now-dns.js

@ -86,7 +86,8 @@ if (argv.help || !subcommand) {
help()
exit(0)
} else {
Promise.resolve().then(async () => {
Promise.resolve()
.then(async () => {
const config = await cfg.read({ token: argv.token })
let token
@ -104,6 +105,10 @@ if (argv.help || !subcommand) {
exit(1)
}
})
.catch(err => {
handleError(err)
process.exit(1)
})
}
async function run({ token, config: { currentTeam, user } }) {

9
bin/now-domains.js

@ -19,7 +19,7 @@ const logo = require('../lib/utils/output/logo')
const promptBool = require('../lib/utils/input/prompt-bool')
const strlen = require('../lib/strlen')
const toHost = require('../lib/to-host')
const { error } = require('../lib/error')
const { handleError, error } = require('../lib/error')
const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'],
@ -150,7 +150,8 @@ if (argv.help || !subcommand) {
help()
exit(0)
} else {
Promise.resolve().then(async () => {
Promise.resolve()
.then(async () => {
const config = await cfg.read({ token: argv.token })
let token
@ -171,6 +172,10 @@ if (argv.help || !subcommand) {
exit(1)
}
})
.catch(err => {
handleError(err)
process.exit(1)
})
}
async function run({ token, config: { currentTeam, user } }) {

9
bin/now-list.js

@ -71,7 +71,8 @@ if (argv.config) {
cfg.setConfigFile(argv.config)
}
Promise.resolve().then(async () => {
Promise.resolve()
.then(async () => {
const config = await cfg.read({ token: argv.token })
let token
@ -88,7 +89,11 @@ Promise.resolve().then(async () => {
error(`Unknown error: ${err}\n${err.stack}`)
process.exit(1)
}
})
})
.catch(err => {
handleError(err)
process.exit(1)
})
async function list({ token, config: { currentTeam, user } }) {
const now = new Now({ apiUrl, token, debug, currentTeam })

6
bin/now-logout.js

@ -9,6 +9,7 @@ const ora = require('ora')
// Utilities
const cfg = require('../lib/cfg')
const logo = require('../lib/utils/output/logo')
const { handleError } = require('../lib/error')
const argv = minimist(process.argv.slice(2), {
string: ['config'],
@ -123,4 +124,7 @@ const logout = async () => {
spinner.succeed('Logged out!')
}
logout()
logout().catch(err => {
handleError(err)
process.exit(1)
})

2
bin/now-logs.js

@ -124,7 +124,7 @@ Promise.resolve()
await printLogs({ token, config })
})
.catch(err => {
error(`Unknown error: ${err.stack}`)
handleError(err)
process.exit(1)
})

9
bin/now-remove.js

@ -85,7 +85,8 @@ if (argv.config) {
cfg.setConfigFile(argv.config)
}
Promise.resolve().then(async () => {
Promise.resolve()
.then(async () => {
const config = await cfg.read({ token: argv.token })
let token
@ -102,7 +103,11 @@ Promise.resolve().then(async () => {
error(`Unknown error: ${err}\n${err.stack}`)
process.exit(1)
}
})
})
.catch(err => {
handleError(err)
process.exit(1)
})
function readConfirmation(matches) {
return new Promise(resolve => {

7
bin/now-scale.js

@ -87,7 +87,8 @@ if (argv.help) {
help()
exit(0)
} else {
Promise.resolve().then(async () => {
Promise.resolve()
.then(async () => {
const config = await cfg.read({ token: argv.token })
let token
@ -109,6 +110,10 @@ if (argv.help) {
exit(1)
}
})
.catch(err => {
handleError(err)
process.exit(1)
})
}
function guessParams() {

7
bin/now-secrets.js

@ -94,7 +94,8 @@ if (argv.help || !subcommand) {
help()
exit(0)
} else {
Promise.resolve().then(async () => {
Promise.resolve()
.then(async () => {
const config = await cfg.read({ token: argv.token })
let token
@ -112,6 +113,10 @@ if (argv.help || !subcommand) {
exit(1)
}
})
.catch(err => {
handleError(err)
process.exit(1)
})
}
async function run({ token, config: { currentTeam, user } }) {

8
bin/now-teams.js

@ -14,6 +14,7 @@ const error = require('../lib/utils/output/error')
const NowTeams = require('../lib/teams')
const logo = require('../lib/utils/output/logo')
const exit = require('../lib/utils/exit')
const { handleError } = require('../lib/error')
const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'],
@ -91,7 +92,8 @@ if (argv.help || !subcommand) {
help()
exit(0)
} else {
Promise.resolve().then(async () => {
Promise.resolve()
.then(async () => {
const config = await cfg.read({ token: argv.token })
let token
@ -113,6 +115,10 @@ if (argv.help || !subcommand) {
exit(1)
}
})
.catch(err => {
handleError(err)
process.exit(1)
})
}
async function run({ token, config: { currentTeam } }) {

8
bin/now-upgrade.js

@ -16,6 +16,7 @@ const error = require('../lib/utils/output/error')
const success = require('../lib/utils/output/success')
const cmd = require('../lib/utils/output/cmd')
const logo = require('../lib/utils/output/logo')
const { handleError } = require('../lib/error')
const { bold } = chalk
@ -83,7 +84,8 @@ if (argv.help) {
help()
exit(0)
} else {
Promise.resolve().then(async () => {
Promise.resolve()
.then(async () => {
const config = await cfg.read({ token: argv.token })
let token
@ -105,6 +107,10 @@ if (argv.help) {
exit(1)
}
})
.catch(err => {
handleError(err)
process.exit(1)
})
}
function buildInquirerChoices(current, until) {

6
bin/now-whoami.js

@ -9,6 +9,7 @@ const cfg = require('../lib/cfg')
const exit = require('../lib/utils/exit')
const cmd = require('../lib/utils/output/cmd')
const logo = require('../lib/utils/output/logo')
const { handleError } = require('../lib/error')
const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'],
@ -71,4 +72,7 @@ async function whoami() {
console.log(name)
}
whoami()
whoami().catch(err => {
handleError(err)
process.exit(1)
})

46
lib/error.js

@ -5,12 +5,16 @@ const chalk = require('chalk')
const error = require('./utils/output/error')
const info = require('./utils/output/info')
function handleError(err) {
function handleError(err, { debug = false } = {}) {
// Coerce Strings to Error instances
if (typeof err === 'string') {
err = new Error(err)
}
if (debug) {
console.log(`> [debug] handling error: ${err.stack}`)
}
if (err.status === 403) {
error(
'Authentication error. Run `now -L` or `now --login` to log-in again.'
@ -24,7 +28,7 @@ function handleError(err) {
error(
'Rate limit exceeded error. Try again in ' +
ms(err.retryAfter * 1000, { long: true }) +
', or upgrade your account by runnung ' +
', or upgrade your account by running ' +
`${chalk.gray('`')}${chalk.cyan('now upgrade')}${chalk.gray('`')}`
)
}
@ -35,11 +39,47 @@ function handleError(err) {
} else if (err.code === 'USER_ABORT') {
info('Aborted')
} else {
error(`Unexpected error. Please try later. (${err.message})`)
error(`Unexpected error. Please try again later. (${err.message})`)
}
}
async function responseError(res) {
let message
let userError
if (res.status >= 400 && res.status < 500) {
let body
try {
body = await res.json()
} catch (err) {
body = {}
}
// Some APIs wrongly return `err` instead of `error`
message = (body.error || body.err || {}).message
userError = true
} else {
userError = false
}
const err = new Error(message || 'Response error')
err.status = res.status
err.userError = userError
if (res.status === 429) {
const retryAfter = res.headers.get('Retry-After')
if (retryAfter) {
err.retryAfter = parseInt(retryAfter, 10)
}
}
return err
}
module.exports = {
handleError,
responseError,
error
}

72
lib/index.js

@ -14,6 +14,7 @@ const retry = require('async-retry')
const splitArray = require('split-array')
const { parse: parseIni } = require('ini')
const { readFile, stat, lstat } = require('fs-extra')
const { responseError } = require('./error')
// Ours
const {
@ -526,12 +527,24 @@ module.exports = class Now extends EventEmitter {
}
async listAliases(deploymentId) {
return this.retry(async () => {
return this.retry(async bail => {
const res = await this._fetch(
deploymentId
? `/now/deployments/${deploymentId}/aliases`
: '/now/aliases'
)
if (res.status >= 400 && res.status < 500) {
if (this._debug) {
console.log('> [debug] bailing on get domain due to %s', res.status)
}
return bail(await responseError(res))
}
if (res.status !== 200) {
throw new Error('API error getting aliases')
}
const body = await res.json()
return body.aliases
})
@ -567,6 +580,17 @@ module.exports = class Now extends EventEmitter {
console.timeEnd(`> [debug] #${attempt} GET /domains`)
}
if (res.status >= 400 && res.status < 500) {
if (this._debug) {
console.log('> [debug] bailing on get domain due to %s', res.status)
}
return bail(await responseError(res))
}
if (res.status !== 200) {
throw new Error('API error getting domains')
}
const body = await res.json()
return body.domains
})
@ -580,6 +604,17 @@ module.exports = class Now extends EventEmitter {
const res = await this._fetch(`/domains/${domain}`)
if (res.status >= 400 && res.status < 500) {
if (this._debug) {
console.log('> [debug] bailing on get domain due to %s', res.status)
}
return bail(await responseError(res))
}
if (res.status !== 200) {
throw new Error('API error getting domain name')
}
if (this._debug) {
console.timeEnd(`> [debug] #${attempt} GET /domains/${domain}`)
}
@ -953,41 +988,6 @@ function toRelative(path, base) {
return relative.replace(/\\/g, '/')
}
async function responseError(res) {
let message
let userError
if (res.status >= 400 && res.status < 500) {
let body
try {
body = await res.json()
} catch (err) {
body = {}
}
// Some APIs wrongly return `err` instead of `error`
message = (body.error || body.err || {}).message
userError = true
} else {
userError = false
}
const err = new Error(message || 'Response error')
err.status = res.status
err.userError = userError
if (res.status === 429) {
const retryAfter = res.headers.get('Retry-After')
if (retryAfter) {
err.retryAfter = parseInt(retryAfter, 10)
}
}
return err
}
function hasNpmStart(pkg) {
return pkg.scripts && (pkg.scripts.start || pkg.scripts['now-start'])
}

24
lib/user.js

@ -1,4 +1,5 @@
const _fetch = require('node-fetch')
const { responseError } = require('./error')
function _filter(data) {
data = data.user
@ -34,19 +35,32 @@ async function get(
fetch = _fetch
}
try {
const res = await fetch(url, { headers })
if (res.status === 403) {
const err = Error(
'Your authentication token is invalid. Try running `now login` to log in again.'
)
err.userError = true
throw err
}
if (res.status >= 400 && res.status < 500) {
const err = await responseError(res)
throw err
}
if (res.status !== 200) {
throw new Error('API Error getting user data')
}
const json = await res.json()
if (filter) {
return _filter(json)
}
return json
} catch (err) {
console.error(err.stack)
return null
}
}
module.exports = {

Loading…
Cancel
Save