6 changed files with 301 additions and 11 deletions
@ -0,0 +1,121 @@ |
|||
// Packages
|
|||
const { italic, bold } = require('chalk') |
|||
|
|||
// Utilities
|
|||
const error = require('../../lib/utils/output/error') |
|||
const wait = require('../../lib/utils/output/wait') |
|||
const cmd = require('../../lib/utils/output/cmd') |
|||
const param = require('../../lib/utils/output/param') |
|||
const info = require('../../lib/utils/output/info') |
|||
const success = require('../../lib/utils/output/success') |
|||
const stamp = require('../../lib/utils/output/stamp') |
|||
const promptBool = require('../../lib/utils/input/prompt-bool') |
|||
const eraseLines = require('../../lib/utils/output/erase-lines') |
|||
const treatBuyError = require('../../lib/utils/domains/treat-buy-error') |
|||
const NowCreditCards = require('../../lib/credit-cards') |
|||
|
|||
module.exports = async function({ domains, args, currentTeam, user, coupon }) { |
|||
const name = args[0] |
|||
let elapsed |
|||
|
|||
if (!name) { |
|||
return error(`Missing domain name. Run ${cmd('now domains help')}`) |
|||
} |
|||
|
|||
const nameParam = param(name) |
|||
let stopSpinner |
|||
|
|||
let price |
|||
let period |
|||
let validCoupon |
|||
try { |
|||
if (coupon) { |
|||
stopSpinner = wait(`Validating coupon ${param(coupon)}`) |
|||
const creditCards = new NowCreditCards({ |
|||
apiUrl: domains._agent._url, |
|||
token: domains._token, |
|||
debug: domains._debug, |
|||
currentTeam |
|||
}) |
|||
const [couponInfo, { cards }] = await Promise.all([ |
|||
domains.coupon(coupon), |
|||
creditCards.ls() |
|||
]) |
|||
stopSpinner() |
|||
|
|||
if (!couponInfo.isValid) { |
|||
return error(`The coupon ${param(coupon)} is invalid`) |
|||
} |
|||
|
|||
if (!couponInfo.canBeUsed) { |
|||
return error(`The coupon ${param(coupon)} has already been used`) |
|||
} |
|||
|
|||
validCoupon = true |
|||
|
|||
if (cards.length === 0) { |
|||
info( |
|||
'You have no credit cards on file. Please add one in order to claim your free domain' |
|||
) |
|||
info(`Your card will ${bold('not')} be charged`) |
|||
|
|||
await require('../now-billing-add')({ |
|||
creditCards, |
|||
currentTeam, |
|||
user, |
|||
clear: true |
|||
}) |
|||
} |
|||
} |
|||
elapsed = stamp() |
|||
stopSpinner = wait(`Checking availability for ${nameParam}`) |
|||
const json = await domains.price(name) |
|||
price = validCoupon ? 0 : json.price |
|||
period = json.period |
|||
} catch (err) { |
|||
stopSpinner() |
|||
return error(err.message) |
|||
} |
|||
|
|||
const available = await domains.status(name) |
|||
|
|||
stopSpinner() |
|||
|
|||
if (!available) { |
|||
return error( |
|||
`The domain ${nameParam} is ${italic('unavailable')}! ${elapsed()}` |
|||
) |
|||
} |
|||
const periodMsg = `${period}yr${period > 1 ? 's' : ''}` |
|||
info( |
|||
`The domain ${nameParam} is ${italic('available')} to buy under ${bold( |
|||
(currentTeam && currentTeam.slug) || user.username || user.email |
|||
)}! ${elapsed()}` |
|||
) |
|||
const confirmation = await promptBool( |
|||
`Buy now for ${bold(`$${price}`)} (${periodMsg})?` |
|||
) |
|||
|
|||
eraseLines(1) |
|||
if (!confirmation) { |
|||
return info('Aborted') |
|||
} |
|||
|
|||
stopSpinner = wait('Purchasing') |
|||
elapsed = stamp() |
|||
try { |
|||
await domains.buy({ name, coupon }) |
|||
} catch (err) { |
|||
stopSpinner() |
|||
return treatBuyError(err) |
|||
} |
|||
|
|||
stopSpinner() |
|||
|
|||
success(`Domain ${nameParam} purchased ${elapsed()}`) |
|||
info( |
|||
`You may now use your domain as an alias to your deployments. Run ${cmd( |
|||
'now alias --help' |
|||
)}` |
|||
) |
|||
} |
@ -0,0 +1,142 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
// Packages
|
|||
const minimist = require('minimist') |
|||
const chalk = require('chalk') |
|||
const fetch = require('node-fetch') |
|||
const ora = require('ora') |
|||
|
|||
// Utilities
|
|||
const cfg = require('../lib/cfg') |
|||
const logo = require('../lib/utils/output/logo') |
|||
const { handleError } = require('../lib/error') |
|||
|
|||
const help = () => { |
|||
console.log(` |
|||
${chalk.bold(`${logo} now logout`)} |
|||
|
|||
${chalk.dim('Options:')} |
|||
|
|||
-h, --help output usage information |
|||
-c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline( |
|||
'FILE' |
|||
)} config file |
|||
|
|||
${chalk.dim('Examples:')} |
|||
|
|||
${chalk.gray('–')} Logout from the CLI: |
|||
|
|||
${chalk.cyan('$ now logout')} |
|||
`)
|
|||
} |
|||
|
|||
let argv |
|||
let apiUrl |
|||
let endpoint |
|||
|
|||
const main = async ctx => { |
|||
argv = minimist(ctx.argv.slice(2), { |
|||
string: ['config'], |
|||
boolean: ['help'], |
|||
alias: { |
|||
help: 'h', |
|||
config: 'c' |
|||
} |
|||
}) |
|||
|
|||
apiUrl = argv.url || 'https://api.zeit.co' |
|||
endpoint = apiUrl + '/www/user/tokens/' |
|||
|
|||
if (argv.config) { |
|||
cfg.setConfigFile(argv.config) |
|||
} |
|||
|
|||
if (argv.help) { |
|||
help() |
|||
process.exit(0) |
|||
} |
|||
|
|||
logout() |
|||
} |
|||
|
|||
module.exports = async ctx => { |
|||
try { |
|||
await main(ctx) |
|||
} catch (err) { |
|||
handleError(err) |
|||
process.exit(1) |
|||
} |
|||
} |
|||
|
|||
const requestHeaders = token => ({ |
|||
headers: { |
|||
Authorization: `bearer ${token}` |
|||
} |
|||
}) |
|||
|
|||
const getTokenId = async token => { |
|||
const result = await fetch(endpoint, requestHeaders(token)) |
|||
const tokenList = await result.json() |
|||
|
|||
if (!tokenList.tokens) { |
|||
return |
|||
} |
|||
|
|||
const tokenInfo = tokenList.tokens.find(t => token === t.token) |
|||
|
|||
if (!tokenInfo) { |
|||
return |
|||
} |
|||
|
|||
return tokenInfo.id |
|||
} |
|||
|
|||
const revokeToken = async (token, tokenId) => { |
|||
const details = { |
|||
method: 'DELETE' |
|||
} |
|||
|
|||
Object.assign(details, requestHeaders(token)) |
|||
const result = await fetch(endpoint + encodeURIComponent(tokenId), details) |
|||
|
|||
if (!result.ok) { |
|||
console.error('Not able to log out') |
|||
} |
|||
} |
|||
|
|||
const logout = async () => { |
|||
const spinner = ora({ |
|||
text: 'Logging out...' |
|||
}).start() |
|||
|
|||
const config = await cfg.read() |
|||
|
|||
try { |
|||
await cfg.removeFile() |
|||
} catch (err) { |
|||
spinner.fail(`Couldn't remove config while logging out`) |
|||
process.exit(1) |
|||
} |
|||
|
|||
let tokenId |
|||
|
|||
try { |
|||
tokenId = await getTokenId(argv.token || config.token) |
|||
} catch (err) { |
|||
spinner.fail('Not able to get token id on logout') |
|||
process.exit(1) |
|||
} |
|||
|
|||
if (!tokenId) { |
|||
return |
|||
} |
|||
|
|||
try { |
|||
await revokeToken(argv.token || config.token, tokenId) |
|||
} catch (err) { |
|||
spinner.fail('Could not revoke token on logout') |
|||
process.exit(1) |
|||
} |
|||
|
|||
spinner.succeed('Logged out!') |
|||
} |
Loading…
Reference in new issue