UNPKG

@liara/cli

Version:

The command line interface for Liara

221 lines (220 loc) 8.82 kB
import ora from 'ora'; import inquirer from 'inquirer'; import { Flags } from '@oclif/core'; import Command from '../../base.js'; import parseJSON from '../../utils/json-parse.js'; import { AVAILABLE_PLATFORMS } from '../../constants.js'; import checkRegexPattern from '../../utils/name-regex.js'; import { createDebugLogger } from '../../utils/output.js'; import getBundlePlanName from '../../utils/set-bundle-plan-names.js'; class AppCreate extends Command { async run() { this.spinner = ora(); const { flags } = await this.parse(AppCreate); const debug = createDebugLogger(flags.debug); await this.setGotConfig(flags); const name = flags.app || (await this.promptAppName()); await this.setGotConfig(flags); const platform = flags.platform || (await this.promptPlatform()); if (!AVAILABLE_PLATFORMS.includes(platform)) { this.error(`Unknown platform: ${platform}`); } const network = flags.network ? await this.getNetwork(flags.network) : await this.promptNetwork(); const planID = flags.plan || (await this.promptPlan()); const bundlePlanID = flags['feature-plan'] || (await this.promptBundlePlan(planID)); const readOnly = flags['read-only'] === 'true' ? true : flags['read-only'] === 'false' ? false : undefined; if (planID === 'free' && bundlePlanID !== 'free') { this.error(`Only "free" feature bundle plan is available for free plan.`); } try { await this.got.post('v1/projects/', { json: { name, planID, platform, bundlePlanID, network: network === null || network === void 0 ? void 0 : network._id, readOnlyRootFilesystem: readOnly, }, }); this.log(`App ${name} created.`); } catch (error) { debug(error.message); const err = parseJSON(error.response.body); if (error.response && error.response.body) { debug(JSON.stringify(error.response.body)); } if (error.response && err.statusCode === 400 && err.message.includes('bundlePlan is not available for free plan.')) { this.error(`The selected feature bundle plan is not available for free plan.`); } if (error.response && error.response.statusCode === 402) { this.error(`Not enough balance. Please charge your account.`); } if (error.response && error.response.statusCode === 404) { this.error(`Could not create the app.`); } if (error.response && error.response.statusCode === 409) { this.error(`The app already exists. Please use a unique name for your app.`); } if (error.response && error.response.statusCode === 403) { if (err.data.code === 'free_plan_platform') { this.error(`The free plan is not available for ${platform} platform.`); } if (err.data.code === 'free_plan_count') { this.error(`You are allowed to create only one app on the free plan`); } } this.error(`Error: Unable to Create App Please try the following steps: 1. Check your internet connection. 2. Ensure you have enough balance. 3. Try again later. 4. If you still have problems, please contact support by submitting a ticket at https://console.liara.ir/tickets`); } } async promptBundlePlan(plan) { this.spinner.start('Loading...'); try { const { plans } = await this.got('v1/me').json(); this.spinner.stop(); const { bundlePlan } = (await inquirer.prompt({ name: 'bundlePlan', type: 'list', message: 'Please select a feature bundle plan:', choices: [ ...Object.keys(plans.projectBundlePlans) .filter((bundlePlan) => { return bundlePlan === plan; }) .map((bundlePlan) => { const planDetails = plans.projectBundlePlans[bundlePlan]; return Object.keys(planDetails) .filter((key) => planDetails[key].available) .map((key) => { const { displayPrice } = planDetails[key]; return { name: `Plan: ${getBundlePlanName(key)}, Price: ${displayPrice.toLocaleString()} Tomans/Month`, value: key, }; }); }) .flat(), ], })); return bundlePlan; } catch (error) { this.spinner.stop(); throw error; } } async promptPlan() { this.spinner.start('Loading...'); try { // TODO: Use proper type for plans const { plans } = await this.got('v1/me').json(); this.spinner.stop(); const { plan } = (await inquirer.prompt({ name: 'plan', type: 'list', message: 'Please select a plan:', choices: [ ...Object.keys(plans.projects) .filter((plan) => { if ((plan === 'free' || plan.includes('g2')) && plans.projects[plan].available && plans.projects[plan].region === 'iran') { return true; } }) .map((plan) => { const availablePlan = plans.projects[plan]; const ram = availablePlan.RAM.amount; const cpu = availablePlan.CPU.amount; const disk = availablePlan.volume; const price = availablePlan.price * 720; const storageClass = availablePlan.storageClass; return { value: plan, name: `RAM: ${ram}${' '.repeat(5 - ram.toString().length)} GB, CPU: ${cpu}${' '.repeat(6 - cpu.toString().length)}Core, Disk: ${disk}${' '.repeat(5 - disk.toString().length) + 'GB'}${storageClass || 'SSD'}, Price: ${price.toLocaleString()}${price ? ' '.repeat(7 - Math.floor(price).toString().length) + 'Tomans/Month' : ''}`, }; }), ], })); return plan; } catch (error) { this.spinner.stop(); throw error; } } async promptPlatform() { this.spinner.start('Loading...'); try { this.spinner.stop(); const { platform } = (await inquirer.prompt({ name: 'platform', type: 'list', message: 'Please select a platform:', choices: [...AVAILABLE_PLATFORMS.map((platform) => platform)], })); return platform; } catch (error) { this.spinner.stop(); throw error; } } async promptAppName() { const { name } = (await inquirer.prompt({ name: 'name', type: 'input', message: 'Enter app name:', validate: (input) => input.length > 2, })); if (!checkRegexPattern(name)) { this.error('Please enter a valid name for your app.'); } return name; } } AppCreate.description = 'create an app'; AppCreate.flags = { ...Command.flags, app: Flags.string({ char: 'a', description: 'app id', }), platform: Flags.string({ description: 'platform', }), plan: Flags.string({ description: 'plan', }), 'feature-plan': Flags.string({ description: 'feature bundle plan', }), network: Flags.string({ char: 'n', description: 'network', }), 'read-only': Flags.string({ char: 'r', description: 'read-only filesystem', options: ['true', 'false'], }), }; AppCreate.aliases = ['create']; export default AppCreate;