#!/usr/bin/env node // Packages const chalk = require('chalk'); const minimist = require('minimist'); const ms = require('ms'); // Ours const login = require('../lib/login'); const cfg = require('../lib/cfg'); const NowPlans = require('../lib/plans'); const indent = require('../lib/indent'); const listInput = require('../lib/utils/input/list'); const code = require('../lib/utils/output/code'); 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 {bold} = chalk const argv = minimist(process.argv.slice(2), { string: ['config', 'token'], boolean: ['help', 'debug'], alias: { help: 'h', config: 'c', debug: 'd', token: 't' } }); const help = () => { console.log( ` ${chalk.bold(`${logo} now upgrade`)} [plan] ${chalk.dim('Options:')} -h, --help Output usage information -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 ${chalk.dim('Examples:')} ${chalk.gray('–')} List available plans and pick one interactively ${chalk.cyan('$ now upgrade')} ${chalk.yellow('NOTE:')} ${chalk.gray('Make sure you have a payment method, or add one:')} ${chalk.cyan(`$ now billing add`)} ${chalk.gray('–')} Pick a specific plan (premium): ${chalk.cyan(`$ now upgrade premium`)} ` ); }; // Options const debug = argv.debug; const apiUrl = argv.url || 'https://api.zeit.co'; if (argv.config) { cfg.setConfigFile(argv.config); } const exit = code => { // We give stdout some time to flush out // because there's a node bug where // stdout writes are asynchronous // https://github.com/nodejs/node/issues/6456 setTimeout(() => process.exit(code || 0), 100); }; if (argv.help) { help(); exit(0); } else { Promise.resolve().then(async () => { const config = await cfg.read(); let token; try { token = argv.token || config.token || (await login(apiUrl)); } catch (err) { error(`Authentication error – ${err.message}`); exit(1); } try { await run({token, config}); } catch (err) { if (err.userError) { error(err.message); } else { error(`Unknown error: ${err.stack}`); } exit(1); } }); } function buildInquirerChoices(current, until) { if (until) { until = until.split(' '); until = ' for ' + chalk.bold(until[0]) + ' more ' + until[1]; } else { until = ''; } const currentText = bold('(current)') let ossName = `OSS ${bold('FREE')}` let premiumName = `Premium ${bold('$15')}` let proName = `Pro ${bold('$50')}` let advancedName = `Advanced ${bold('$200')}` switch (current) { case 'oss': { ossName += indent(currentText, 6) break } case 'premium': { premiumName += indent(currentText, 3) break } case 'pro': { proName += indent(currentText, 7) break } case 'advanced': { advancedName += indent(currentText, 1) break } default: { ossName += indent(currentText, 6) } } return [ { name: ossName, value: 'oss', short: `OSS ${bold('FREE')}` }, { name: premiumName, value: 'premium', short: `Premium ${bold('$15')}` }, { name: proName, value: 'pro', short: `Pro ${bold('$50')}` }, { name: advancedName, value: 'advanced', short: `Advanced ${bold('$200')}` }, ]; } async function run({token, config: {currentTeam, user}}) { const args = argv._; if (args.length > 1) { error('Invalid number of arguments'); return exit(1); } const start = new Date(); const plans = new NowPlans({ apiUrl, token, debug, currentTeam }); let planId = args[0]; if (![undefined, 'oss', 'premium', 'pro', 'advanced'].includes(planId)) { error(`Invalid plan name – should be ${code('oss')} or ${code('premium')}`); return exit(1); } const currentPlan = await plans.getCurrent(); if (planId === undefined) { const elapsed = ms(new Date() - start); let message = `For more info, please head to https://zeit.co`; message = currentTeam ? `${message}/${currentTeam.slug}/settings/plan` : `${message}/account/plan` message += `\n> Select a plan for ${ bold( (currentTeam && currentTeam.slug) || user.username || user.email ) } ${chalk.gray(`[${elapsed}]`)}`; const choices = buildInquirerChoices(currentPlan.id, currentPlan.until); planId = await listInput({ message, choices, separator: false, abort: 'end' }); } if ( planId === undefined || (planId === currentPlan.id && currentPlan.until === undefined) ) { return console.log('No changes made'); } let newPlan; try { newPlan = await plans.set(planId); } catch (err) { if (err.code === 'customer_not_found' || err.code === 'source_not_found') { error( `You have no payment methods available. Run ${cmd('now billing add')} to add one` ); } else { error(`An unknow error occured. Please try again later ${err.message}`); } plans.close(); return; } if (currentPlan.until && newPlan.id !== 'oss') { success( `The cancelation has been undone. You're back on the ${chalk.bold(`${newPlan.name} plan`)}` ); } else if (newPlan.until) { success( `Your plan will be switched to ${chalk.bold(newPlan.name)} in ${chalk.bold(newPlan.until)}. Your card will not be charged again` ); } else { success(`You're now on the ${chalk.bold(`${newPlan.name} plan`)}`); } plans.close(); }