UNPKG

@liara/cli

Version:

The command line interface for Liara

289 lines (288 loc) 11.3 kB
import ora from 'ora'; import inquirer from 'inquirer'; import { ux } from '@oclif/core'; import { Flags } from '@oclif/core'; import Command from '../../base.js'; import parseJSON from '../../utils/json-parse.js'; import { createDebugLogger } from '../../utils/output.js'; import checkRegexPattern from '../../utils/name-regex.js'; class Create extends Command { async run() { this.spinner = ora(); const { flags } = await this.parse(Create); const debug = createDebugLogger(flags.debug); await this.setGotConfig(flags); const hostname = flags.name || (await this.promptHostname()); const type = flags.type || (await this.promptType()); const version = flags.version || (await this.promptVersion(type)); const network = flags.network ? await this.getNetwork(flags.network) : await this.promptNetwork(); const publicNetwork = flags['public-network'] || (await this.promptPublicNetwork()) === 'y'; const planID = flags.plan || (await this.promptPlan(type)); const bundlePlanID = flags['feature-plan'] || (await this.promptBundlePlan(planID)); const sayYes = flags.yes; if (planID === 'free' && bundlePlanID !== 'free') { this.error(`Only "free" feature bundle plan is available for free plan.`); } try { const tableData = [ { type, version, hostname, network: network === null || network === void 0 ? void 0 : network.name, publicNetwork: publicNetwork.toString(), }, ]; const tableConfig = { Hostname: { get: (row) => row.hostname }, Type: { get: (row) => row.type }, Version: { get: (row) => row.version }, Network: { get: (row) => row.network }, 'Public Network': { get: (row) => row.publicNetwork }, }; ux.table(tableData, tableConfig, { title: 'Database Specification', }); if (!sayYes && (await this.promptContinue()) === 'n') { this.log('Operation cancelled'); return; } await this.got.post(Create.PATH, { json: { hostname, planID, bundlePlanID, publicNetwork, type, version, network: network === null || network === void 0 ? void 0 : network._id, }, }); this.log(`Database ${hostname} 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 === 404) { this.error(`Could not create the database.`); } if (error.response && error.response.statusCode === 409) { this.error(`The database already exists. Please use a unique name for your database.`); } if (error.response && error.response.statusCode === 403 && error.response.body) { const body = JSON.parse(error.response.body); if (body.data.code === 'free_plan_platform') { this.error(`The free plan is not available for ${type} database.`); } if (body.data.code === 'free_plan_count') { this.error(`You are allowed to create only one database on the free plan`); } } this.error(`Unable to Create Database Please follow these steps: 1. Check your internet connection. 2. Ensure you have enough balance. 2. Try again later. 3. 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: plan === 'free' ? [ { name: `Plan: free, Price: 0 Tomans/Month`, value: 'free', }, ] : [ ...Object.keys(plans.databaseBundlePlans) .filter((bundlePlan) => { return bundlePlan === plan; }) .map((bundlePlan) => { const planDetails = plans.databaseBundlePlans[bundlePlan]; return Object.keys(planDetails) .filter((key) => planDetails[key].available) .map((key) => { const { displayPrice } = planDetails[key]; return { name: `Plan: ${key}, Price: ${displayPrice.toLocaleString()} Tomans/Month`, value: key, }; }); }) .flat(), ], })); return bundlePlan; } catch (error) { this.spinner.stop(); throw error; } } async promptHostname() { const { name } = (await inquirer.prompt({ name: 'name', type: 'input', message: 'Enter name:', validate: (input) => input.length > 2, })); if (!checkRegexPattern(name)) { this.error('Please enter a valid name for your app.'); } return name; } async promptPlan(databaseType) { this.spinner.start('Loading...'); try { 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.databases) .filter((plan) => { if ((plan === 'free' || plan.includes('g2')) && plans.databases[plan].available && plans.databases[plan].supports.includes(databaseType)) { return true; } }) .map((plan) => { const availablePlan = plans.databases[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 promptPublicNetwork() { const { is_public } = (await inquirer.prompt({ name: 'is_public', type: 'input', message: 'use public network?(y/n):', validate: (input) => input === 'y' || input === 'n', })); return is_public; } async promptType() { this.spinner.start('Loading...'); try { const { databaseVersions } = await this.got('v1/me').json(); this.spinner.stop(); const { databaseType } = (await inquirer.prompt({ name: 'databaseType', type: 'list', message: 'Please select a database type:', choices: [...Object.keys(databaseVersions)], })); return databaseType; } catch (error) { this.spinner.stop(); throw error; } } async promptVersion(type) { this.spinner.start('Loading...'); try { const { databaseVersions } = await this.got('v1/me').json(); this.spinner.stop(); const { databaseVersion } = (await inquirer.prompt({ name: 'databaseVersion', type: 'list', message: 'Please select a version:', choices: [ ...databaseVersions[type].map((obj) => obj.value), ], })); return databaseVersion; } catch (error) { this.spinner.stop(); throw error; } } async promptContinue() { const { yes } = (await inquirer.prompt({ name: 'yes', type: 'input', message: 'continue? (y/n):', validate: (input) => input === 'y' || input === 'n', })); return yes; } } Create.description = 'create a new database'; Create.PATH = 'v1/databases'; Create.flags = { ...Command.flags, name: Flags.string({ char: 'n', description: 'name of your database', }), plan: Flags.string({ description: 'plan', }), 'public-network': Flags.boolean({ description: 'use public network or not', }), 'feature-plan': Flags.string({ description: 'feature bundle plan', }), type: Flags.string({ char: 't', description: 'choose which database to use', }), version: Flags.string({ char: 'v', description: 'version of the database', }), yes: Flags.boolean({ char: 'y', description: 'say yes to continue prompt', }), network: Flags.string({ description: 'network', }), }; export default Create;