diff --git a/package.json b/package.json index 5a4ddf6..d2f3cd8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@dokploy/cli", "description": "A CLI to manage dokploy server remotely", - "version": "v0.2.7", + "version": "v0.2.8", "author": "Mauricio Siu", "licenses": [{ "type": "MIT", diff --git a/src/commands/app/create.ts b/src/commands/app/create.ts index 91734f8..40b77c7 100644 --- a/src/commands/app/create.ts +++ b/src/commands/app/create.ts @@ -22,6 +22,11 @@ export default class AppCreate extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), name: Flags.string({ char: "n", description: "Application name", @@ -46,13 +51,16 @@ export default class AppCreate extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(AppCreate); - let { projectId, name, description, appName } = flags; + let { projectId, environmentId, name, description, appName } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !name || !appName) { + if (!projectId || !environmentId || !name || !appName) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -65,7 +73,30 @@ export default class AppCreate extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); + } + + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } + + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + environmentId = environment.environmentId; } if (!name || !appName) { @@ -128,6 +159,7 @@ export default class AppCreate extends Command { appDescription: description, appName, projectId, + environmentId, }, }, { diff --git a/src/commands/app/delete.ts b/src/commands/app/delete.ts index 1d07438..af2a87b 100644 --- a/src/commands/app/delete.ts +++ b/src/commands/app/delete.ts @@ -3,7 +3,7 @@ import axios from "axios"; import chalk from "chalk"; import inquirer from "inquirer"; -import { getProject, getProjects } from "../../utils/shared.js"; +import { getProject, getProjects, type Application } from "../../utils/shared.js"; import { readAuthConfig } from "../../utils/utils.js"; import type { Answers } from "./create.js"; @@ -21,6 +21,11 @@ export default class AppDelete extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), applicationId: Flags.string({ char: 'a', description: 'ID of the application to delete', @@ -36,13 +41,17 @@ export default class AppDelete extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(AppDelete); - let { projectId, applicationId } = flags; + let { projectId, environmentId, applicationId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !applicationId) { + if (!projectId || !environmentId || !applicationId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -55,20 +64,44 @@ export default class AppDelete extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.applications.length === 0) { - this.error(chalk.yellow("No applications found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar application del environment if (!applicationId) { + if (!selectedEnvironment?.applications || selectedEnvironment.applications.length === 0) { + this.error(chalk.yellow("No applications found in this environment.")); + } + const appAnswers = await inquirer.prompt([ { - // @ts-ignore - choices: projectSelected.applications.map((app) => ({ + choices: selectedEnvironment.applications.map((app: Application) => ({ name: app.name, value: app.applicationId, })), diff --git a/src/commands/app/deploy.ts b/src/commands/app/deploy.ts index 66206bb..479f7f0 100644 --- a/src/commands/app/deploy.ts +++ b/src/commands/app/deploy.ts @@ -1,7 +1,7 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../utils/utils.js"; import chalk from "chalk"; -import { getProject, getProjects } from "../../utils/shared.js"; +import { getProject, getProjects, type Application } from "../../utils/shared.js"; import inquirer from "inquirer"; import type { Answers } from "./create.js"; import axios from "axios"; @@ -26,6 +26,11 @@ export default class AppDeploy extends Command { description: 'ID of the project', required: false, }), + environmentId: Flags.string({ + char: 'e', + description: 'ID of the environment', + required: false, + }), skipConfirm: Flags.boolean({ char: 'y', description: 'Skip confirmation prompt', @@ -36,13 +41,17 @@ export default class AppDeploy extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(AppDeploy); - let { projectId, applicationId } = flags; + let { projectId, applicationId, environmentId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !applicationId) { + if (!projectId || !applicationId || !environmentId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -55,20 +64,44 @@ export default class AppDeploy extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.applications.length === 0) { - this.error(chalk.yellow("No applications found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar application del environment if (!applicationId) { + if (!selectedEnvironment?.applications || selectedEnvironment.applications.length === 0) { + this.error(chalk.yellow("No applications found in this environment.")); + } + const appAnswers = await inquirer.prompt([ { - // @ts-ignore - choices: projectSelected.applications.map((app) => ({ + choices: selectedEnvironment.applications.map((app: Application) => ({ name: app.name, value: app.applicationId, })), diff --git a/src/commands/app/stop.ts b/src/commands/app/stop.ts index 1efd3ae..b642e24 100644 --- a/src/commands/app/stop.ts +++ b/src/commands/app/stop.ts @@ -2,7 +2,7 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../utils/utils.js"; import chalk from "chalk"; import inquirer from "inquirer"; -import { getProject, getProjects } from "../../utils/shared.js"; +import { getProject, getProjects, type Application } from "../../utils/shared.js"; import type { Answers } from "./create.js"; import axios from "axios"; @@ -17,6 +17,11 @@ export default class AppStop extends Command { description: 'ID of the project', required: false, }), + environmentId: Flags.string({ + char: 'e', + description: 'ID of the environment', + required: false, + }), applicationId: Flags.string({ char: 'a', description: 'ID of the application to stop', @@ -32,13 +37,17 @@ export default class AppStop extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(AppStop); - let { projectId, applicationId } = flags; + let { projectId, environmentId, applicationId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !applicationId) { + if (!projectId || !environmentId || !applicationId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -51,19 +60,44 @@ export default class AppStop extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.applications.length === 0) { - this.error(chalk.yellow("No applications found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar application del environment if (!applicationId) { + if (!selectedEnvironment?.applications || selectedEnvironment.applications.length === 0) { + this.error(chalk.yellow("No applications found in this environment.")); + } + const appAnswers = await inquirer.prompt([ { - choices: projectSelected.applications.map((app: { name: string; applicationId: string }) => ({ + choices: selectedEnvironment.applications.map((app: Application) => ({ name: app.name, value: app.applicationId, })), diff --git a/src/commands/authenticate.ts b/src/commands/authenticate.ts index f17e0e6..c57eb49 100644 --- a/src/commands/authenticate.ts +++ b/src/commands/authenticate.ts @@ -103,7 +103,3 @@ export default class Authenticate extends Command { } } } -// curl -X 'GET' \ -// 'https://panel.jinza.app/api/project.all' \ -// -H 'accept: application/json' \ -// -H 'x-api-key: EawCkTREMhxoAqvCxJFZurgCGoDZPjYHHrLgUPghRjJTpXLaahFdhCOGfABZXTRP' \ No newline at end of file diff --git a/src/commands/database/mariadb/create.ts b/src/commands/database/mariadb/create.ts index 7ea7101..d2c4811 100644 --- a/src/commands/database/mariadb/create.ts +++ b/src/commands/database/mariadb/create.ts @@ -3,7 +3,7 @@ import axios from "axios"; import chalk from "chalk"; import inquirer from "inquirer"; import { readAuthConfig } from "../../../utils/utils.js"; -import { getProjects } from "../../../utils/shared.js"; +import { getProjects, type Database } from "../../../utils/shared.js"; import { slugify } from "../../../utils/slug.js"; import type { Answers } from "../../app/create.js"; @@ -18,6 +18,11 @@ export default class DatabaseMariadbCreate extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), name: Flags.string({ char: "n", description: "Database name", @@ -64,6 +69,7 @@ export default class DatabaseMariadbCreate extends Command { const { flags } = await this.parse(DatabaseMariadbCreate); let { projectId, + environmentId, name, databaseName, description, @@ -75,10 +81,13 @@ export default class DatabaseMariadbCreate extends Command { } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !name || !databaseName || !appName) { + if (!projectId || !environmentId || !name || !databaseName || !appName) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -91,7 +100,30 @@ export default class DatabaseMariadbCreate extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); + } + + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } + + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + environmentId = environment.environmentId; } if (!name || !databaseName || !appName) { @@ -195,6 +227,7 @@ export default class DatabaseMariadbCreate extends Command { dockerImage, appName, projectId, + environmentId, }, }, { diff --git a/src/commands/database/mariadb/delete.ts b/src/commands/database/mariadb/delete.ts index fa2a12e..87c9ba6 100644 --- a/src/commands/database/mariadb/delete.ts +++ b/src/commands/database/mariadb/delete.ts @@ -3,7 +3,7 @@ import axios from "axios"; import chalk from "chalk"; import inquirer from "inquirer"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import { readAuthConfig } from "../../../utils/utils.js"; export default class DatabaseMariadbDelete extends Command { @@ -18,6 +18,11 @@ export default class DatabaseMariadbDelete extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), mariadbId: Flags.string({ char: "m", description: "ID of the MariaDB instance to delete", @@ -33,12 +38,16 @@ export default class DatabaseMariadbDelete extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseMariadbDelete); - let { projectId, mariadbId } = flags; + let { projectId, environmentId, mariadbId } = flags; - if (!projectId || !mariadbId) { + if (!projectId || !environmentId || !mariadbId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const answers = await inquirer.prompt([ { @@ -51,30 +60,54 @@ export default class DatabaseMariadbDelete extends Command { type: "list", }, ]); + selectedProject = projects.find(p => p.projectId === answers.selectedProject); projectId = answers.selectedProject; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (!projectSelected.mariadb || projectSelected.mariadb.length === 0) { - this.error(chalk.yellow("No MariaDB instances found in this project.")); - } + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); + } + // 3. Seleccionar MariaDB del environment if (!mariadbId) { + if (!selectedEnvironment?.mariadb || selectedEnvironment.mariadb.length === 0) { + this.error(chalk.yellow("No MariaDB instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ - { - // @ts-ignore - choices: projectSelected.mariadb.map((db) => ({ - name: db.name, - value: db.mariadbId, - })), - message: "Select the MariaDB instance to delete:", - name: "selectedDb", - type: "list", - }, - ]); - mariadbId = dbAnswers.selectedDb; - } + { + choices: selectedEnvironment.mariadb.map((db: Database) => ({ + name: db.name, + value: db.mariadbId, + })), + message: "Select the MariaDB instance to delete:", + name: "selectedDb", + type: "list", + }, + ]); + mariadbId = dbAnswers.selectedDb; + } } if (!flags.skipConfirm) { diff --git a/src/commands/database/mariadb/deploy.ts b/src/commands/database/mariadb/deploy.ts index 0209e53..bc54751 100644 --- a/src/commands/database/mariadb/deploy.ts +++ b/src/commands/database/mariadb/deploy.ts @@ -1,7 +1,7 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; import chalk from "chalk"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; import type { Answers } from "../../app/create.js"; import axios from "axios"; @@ -17,6 +17,11 @@ export default class DatabaseMariadbDeploy extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), mariadbId: Flags.string({ char: "m", description: "ID of the MariaDB instance to deploy", @@ -32,13 +37,17 @@ export default class DatabaseMariadbDeploy extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseMariadbDeploy); - let { projectId, mariadbId } = flags; + let { projectId, environmentId, mariadbId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !mariadbId) { + if (!projectId || !environmentId || !mariadbId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -51,20 +60,44 @@ export default class DatabaseMariadbDeploy extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.mariadb.length === 0) { - this.error(chalk.yellow("No MariaDB instances found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar MariaDB del environment if (!mariadbId) { + if (!selectedEnvironment?.mariadb || selectedEnvironment.mariadb.length === 0) { + this.error(chalk.yellow("No MariaDB instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ { - // @ts-ignore - choices: projectSelected.mariadb.map((db) => ({ + choices: selectedEnvironment.mariadb.map((db: Database) => ({ name: db.name, value: db.mariadbId, })), diff --git a/src/commands/database/mariadb/stop.ts b/src/commands/database/mariadb/stop.ts index cbc66ea..f5c9d7e 100644 --- a/src/commands/database/mariadb/stop.ts +++ b/src/commands/database/mariadb/stop.ts @@ -2,7 +2,7 @@ import { Command, Flags } from "@oclif/core"; import chalk from "chalk"; import inquirer from "inquirer"; import axios from "axios"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import { readAuthConfig } from "../../../utils/utils.js"; import type { Answers } from "../../app/create.js"; @@ -17,6 +17,11 @@ export default class DatabaseMariadbStop extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), mariadbId: Flags.string({ char: "m", description: "ID of the MariaDB instance to stop", @@ -32,13 +37,17 @@ export default class DatabaseMariadbStop extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseMariadbStop); - let { projectId, mariadbId } = flags; + let { projectId, environmentId, mariadbId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !mariadbId) { + if (!projectId || !environmentId || !mariadbId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -51,30 +60,54 @@ export default class DatabaseMariadbStop extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.mariadb.length === 0) { - this.error(chalk.yellow("No MariaDB instances found in this project.")); - } + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); + } + // 3. Seleccionar MariaDB del environment if (!mariadbId) { + if (!selectedEnvironment?.mariadb || selectedEnvironment.mariadb.length === 0) { + this.error(chalk.yellow("No MariaDB instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ - { - // @ts-ignore - choices: projectSelected.mariadb.map((db) => ({ - name: db.name, - value: db.mariadbId, - })), - message: "Select the MariaDB instance to stop:", - name: "selectedDb", - type: "list", - }, - ]); - mariadbId = dbAnswers.selectedDb; - } + { + choices: selectedEnvironment.mariadb.map((db: Database) => ({ + name: db.name, + value: db.mariadbId, + })), + message: "Select the MariaDB instance to stop:", + name: "selectedDb", + type: "list", + }, + ]); + mariadbId = dbAnswers.selectedDb; + } } // Confirmar si no se especifica --skipConfirm diff --git a/src/commands/database/mongo/create.ts b/src/commands/database/mongo/create.ts index 45ccc37..30ebd1b 100644 --- a/src/commands/database/mongo/create.ts +++ b/src/commands/database/mongo/create.ts @@ -3,7 +3,7 @@ import axios from "axios"; import chalk from "chalk"; import inquirer from "inquirer"; import { readAuthConfig } from "../../../utils/utils.js"; -import { getProjects } from "../../../utils/shared.js"; +import { getProjects, type Database } from "../../../utils/shared.js"; import { slugify } from "../../../utils/slug.js"; import type { Answers } from "../../app/create.js"; @@ -18,6 +18,11 @@ export default class DatabaseMongoCreate extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), name: Flags.string({ char: "n", description: "Database name", @@ -60,6 +65,7 @@ export default class DatabaseMongoCreate extends Command { const { flags } = await this.parse(DatabaseMongoCreate); let { projectId, + environmentId, name, databaseName, description, @@ -70,10 +76,13 @@ export default class DatabaseMongoCreate extends Command { } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !name || !databaseName || !appName || !databasePassword) { + if (!projectId || !environmentId || !name || !databaseName || !appName || !databasePassword) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -86,9 +95,32 @@ export default class DatabaseMongoCreate extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } + + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + environmentId = environment.environmentId; + } + if (!name || !databaseName || !appName || !databasePassword) { const dbDetails = await inquirer.prompt([ { @@ -182,6 +214,7 @@ export default class DatabaseMongoCreate extends Command { dockerImage, appName, projectId, + environmentId, }, }, { diff --git a/src/commands/database/mongo/delete.ts b/src/commands/database/mongo/delete.ts index dbbff26..1fe34eb 100644 --- a/src/commands/database/mongo/delete.ts +++ b/src/commands/database/mongo/delete.ts @@ -4,7 +4,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { readAuthConfig } from "../../../utils/utils.js"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; export default class DatabaseMongoDelete extends Command { static description = "Delete a MongoDB database from a project."; @@ -20,6 +20,11 @@ export default class DatabaseMongoDelete extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), mongoId: Flags.string({ char: "m", description: "ID of the MongoDB instance to delete", @@ -35,13 +40,17 @@ export default class DatabaseMongoDelete extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseMongoDelete); - let { projectId, mongoId } = flags; + let { projectId, environmentId, mongoId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !mongoId) { + if (!projectId || !environmentId || !mongoId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const answers = await inquirer.prompt([ { @@ -54,20 +63,44 @@ export default class DatabaseMongoDelete extends Command { type: "list", }, ]); + selectedProject = projects.find(p => p.projectId === answers.selectedProject); projectId = answers.selectedProject; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (!projectSelected.mongo || projectSelected.mongo.length === 0) { - this.error(chalk.yellow("No MongoDB instances found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar MongoDB del environment if (!mongoId) { + if (!selectedEnvironment?.mongo || selectedEnvironment.mongo.length === 0) { + this.error(chalk.yellow("No MongoDB instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ { - // @ts-ignore - choices: projectSelected.mongo.map((db) => ({ + choices: selectedEnvironment.mongo.map((db: Database) => ({ name: db.name, value: db.mongoId, })), diff --git a/src/commands/database/mongo/deploy.ts b/src/commands/database/mongo/deploy.ts index dd4d074..7353d7a 100644 --- a/src/commands/database/mongo/deploy.ts +++ b/src/commands/database/mongo/deploy.ts @@ -1,7 +1,7 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; import chalk from "chalk"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; import type { Answers } from "../../app/create.js"; import axios from "axios"; @@ -17,6 +17,11 @@ export default class DatabaseMongoDeploy extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), mongoId: Flags.string({ char: "m", description: "ID of the MongoDB instance to deploy", @@ -32,13 +37,17 @@ export default class DatabaseMongoDeploy extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseMongoDeploy); - let { projectId, mongoId } = flags; + let { projectId, environmentId, mongoId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !mongoId) { + if (!projectId || !environmentId || !mongoId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -51,20 +60,44 @@ export default class DatabaseMongoDeploy extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.mongo.length === 0) { - this.error(chalk.yellow("No MongoDB instances found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar MongoDB del environment if (!mongoId) { + if (!selectedEnvironment?.mongo || selectedEnvironment.mongo.length === 0) { + this.error(chalk.yellow("No MongoDB instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ { - // @ts-ignore - choices: projectSelected.mongo.map((db) => ({ + choices: selectedEnvironment.mongo.map((db: Database) => ({ name: db.name, value: db.mongoId, })), diff --git a/src/commands/database/mongo/stop.ts b/src/commands/database/mongo/stop.ts index 0ead2aa..e26f9ce 100644 --- a/src/commands/database/mongo/stop.ts +++ b/src/commands/database/mongo/stop.ts @@ -2,7 +2,7 @@ import { Command, Flags } from "@oclif/core"; import chalk from "chalk"; import inquirer from "inquirer"; import axios from "axios"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import { readAuthConfig } from "../../../utils/utils.js"; import type { Answers } from "../../app/create.js"; @@ -17,6 +17,11 @@ export default class DatabaseMongoStop extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), mongoId: Flags.string({ char: "m", description: "ID of the MongoDB instance to stop", @@ -32,13 +37,17 @@ export default class DatabaseMongoStop extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseMongoStop); - let { projectId, mongoId } = flags; + let { projectId, environmentId, mongoId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !mongoId) { + if (!projectId || !environmentId || !mongoId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -51,30 +60,54 @@ export default class DatabaseMongoStop extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.mongo.length === 0) { - this.error(chalk.yellow("No MongoDB instances found in this project.")); - } + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); + } + // 3. Seleccionar MongoDB del environment if (!mongoId) { + if (!selectedEnvironment?.mongo || selectedEnvironment.mongo.length === 0) { + this.error(chalk.yellow("No MongoDB instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ - { - // @ts-ignore - choices: projectSelected.mongo.map((db) => ({ - name: db.name, - value: db.mongoId, - })), - message: "Select the MongoDB instance to stop:", - name: "selectedDb", - type: "list", - }, - ]); - mongoId = dbAnswers.selectedDb; - } + { + choices: selectedEnvironment.mongo.map((db: Database) => ({ + name: db.name, + value: db.mongoId, + })), + message: "Select the MongoDB instance to stop:", + name: "selectedDb", + type: "list", + }, + ]); + mongoId = dbAnswers.selectedDb; + } } // Confirmar si no se especifica --skipConfirm diff --git a/src/commands/database/mysql/create.ts b/src/commands/database/mysql/create.ts index 525758b..402e8cb 100644 --- a/src/commands/database/mysql/create.ts +++ b/src/commands/database/mysql/create.ts @@ -5,7 +5,7 @@ import inquirer from "inquirer"; import { slugify } from "../../../utils/slug.js"; import { readAuthConfig } from "../../../utils/utils.js"; -import { getProjects } from "../../../utils/shared.js"; +import { getProjects, type Database } from "../../../utils/shared.js"; import type { Answers } from "../../app/create.js"; export default class DatabaseMysqlCreate extends Command { @@ -19,6 +19,11 @@ export default class DatabaseMysqlCreate extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), name: Flags.string({ char: "n", description: "Database name", @@ -65,6 +70,7 @@ export default class DatabaseMysqlCreate extends Command { const { flags } = await this.parse(DatabaseMysqlCreate); let { projectId, + environmentId, name, databaseName, description, @@ -76,10 +82,13 @@ export default class DatabaseMysqlCreate extends Command { } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !name || !databaseName || !appName || !databasePassword || !databaseRootPassword) { + if (!projectId || !environmentId || !name || !databaseName || !appName || !databasePassword || !databaseRootPassword) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -92,7 +101,30 @@ export default class DatabaseMysqlCreate extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); + } + + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } + + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + environmentId = environment.environmentId; } if (!name || !databaseName || !appName || !databasePassword || !databaseRootPassword) { @@ -197,6 +229,7 @@ export default class DatabaseMysqlCreate extends Command { dockerImage, appName, projectId, + environmentId, }, }, { diff --git a/src/commands/database/mysql/delete.ts b/src/commands/database/mysql/delete.ts index b896afd..c05c442 100644 --- a/src/commands/database/mysql/delete.ts +++ b/src/commands/database/mysql/delete.ts @@ -4,7 +4,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { readAuthConfig } from "../../../utils/utils.js"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; export default class DatabaseMysqlDelete extends Command { static description = "Delete a MySQL database from a project."; @@ -20,6 +20,11 @@ export default class DatabaseMysqlDelete extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), mysqlId: Flags.string({ char: "i", description: "ID of the MySQL database", @@ -35,13 +40,17 @@ export default class DatabaseMysqlDelete extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseMysqlDelete); - let { projectId, mysqlId } = flags; + let { projectId, environmentId, mysqlId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !mysqlId) { + if (!projectId || !environmentId || !mysqlId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const answers = await inquirer.prompt([ { @@ -54,20 +63,44 @@ export default class DatabaseMysqlDelete extends Command { type: "list", }, ]); + selectedProject = projects.find(p => p.projectId === answers.selectedProject); projectId = answers.selectedProject; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (!projectSelected.mysql || projectSelected.mysql.length === 0) { - this.error(chalk.yellow("No MySQL instances found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar MySQL del environment if (!mysqlId) { + if (!selectedEnvironment?.mysql || selectedEnvironment.mysql.length === 0) { + this.error(chalk.yellow("No MySQL instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ { - // @ts-ignore - choices: projectSelected.mysql.map((db) => ({ + choices: selectedEnvironment.mysql.map((db: Database) => ({ name: db.name, value: db.mysqlId, })), diff --git a/src/commands/database/mysql/deploy.ts b/src/commands/database/mysql/deploy.ts index 6e63b4e..747027b 100644 --- a/src/commands/database/mysql/deploy.ts +++ b/src/commands/database/mysql/deploy.ts @@ -1,7 +1,7 @@ -import { Command } from "@oclif/core"; +import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; import chalk from "chalk"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; import type { Answers } from "../../app/create.js"; import axios from "axios"; @@ -11,16 +11,43 @@ export default class DatabaseMysqlDeploy extends Command { static examples = ["$ <%= config.bin %> app deploy"]; + static flags = { + projectId: Flags.string({ + char: "p", + description: "ID of the project", + required: false, + }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), + mysqlId: Flags.string({ + char: "m", + description: "ID of the MySQL instance to deploy", + required: false, + }), + skipConfirm: Flags.boolean({ + char: "y", + description: "Skip confirmation prompt", + default: false, + }), + }; + public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseMysqlDeploy); - let { projectId, mysqlId } = flags; + let { projectId, environmentId, mysqlId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !mysqlId) { + if (!projectId || !environmentId || !mysqlId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -33,20 +60,44 @@ export default class DatabaseMysqlDeploy extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.mysql.length === 0) { - this.error(chalk.yellow("No MySQL instances found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar MySQL del environment if (!mysqlId) { + if (!selectedEnvironment?.mysql || selectedEnvironment.mysql.length === 0) { + this.error(chalk.yellow("No MySQL instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ { - // @ts-ignore - choices: projectSelected.mysql.map((db) => ({ + choices: selectedEnvironment.mysql.map((db: Database) => ({ name: db.name, value: db.mysqlId, })), diff --git a/src/commands/database/mysql/stop.ts b/src/commands/database/mysql/stop.ts index 22173fa..50ca2f6 100644 --- a/src/commands/database/mysql/stop.ts +++ b/src/commands/database/mysql/stop.ts @@ -1,8 +1,8 @@ -import { Command } from "@oclif/core"; +import { Command, Flags } from "@oclif/core"; import chalk from "chalk"; import inquirer from "inquirer"; import axios from "axios"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import { readAuthConfig } from "../../../utils/utils.js"; import type { Answers } from "../../app/create.js"; @@ -11,16 +11,43 @@ export default class DatabaseMysqlStop extends Command { static examples = ["$ <%= config.bin %> mysql stop"]; + static flags = { + projectId: Flags.string({ + char: "p", + description: "ID of the project", + required: false, + }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), + mysqlId: Flags.string({ + char: "i", + description: "ID of the MySQL database", + required: false, + }), + skipConfirm: Flags.boolean({ + char: "y", + description: "Skip confirmation", + required: false, + }), + }; + public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseMysqlStop); - let { projectId, mysqlId } = flags; + let { projectId, environmentId, mysqlId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !mysqlId) { + if (!projectId || !environmentId || !mysqlId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -33,20 +60,44 @@ export default class DatabaseMysqlStop extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.mysql.length === 0) { - this.error(chalk.yellow("No MySQL instances found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar MySQL del environment if (!mysqlId) { + if (!selectedEnvironment?.mysql || selectedEnvironment.mysql.length === 0) { + this.error(chalk.yellow("No MySQL instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ { - // @ts-ignore - choices: projectSelected.mysql.map((db) => ({ + choices: selectedEnvironment.mysql.map((db: Database) => ({ name: db.name, value: db.mysqlId, })), diff --git a/src/commands/database/postgres/create.ts b/src/commands/database/postgres/create.ts index f0e96ff..55db1b7 100644 --- a/src/commands/database/postgres/create.ts +++ b/src/commands/database/postgres/create.ts @@ -4,7 +4,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { slugify } from "../../../utils/slug.js"; import { readAuthConfig } from "../../../utils/utils.js"; -import { getProjects } from "../../../utils/shared.js"; +import { getProjects, type Database } from "../../../utils/shared.js"; import type { Answers } from "../../app/create.js"; export default class DatabasePostgresCreate extends Command { static description = "Create a new PostgreSQL database within a project."; @@ -17,6 +17,11 @@ export default class DatabasePostgresCreate extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), name: Flags.string({ char: "n", description: "Database name", @@ -59,6 +64,7 @@ export default class DatabasePostgresCreate extends Command { const { flags } = await this.parse(DatabasePostgresCreate); let { projectId, + environmentId, name, databaseName, description, @@ -69,10 +75,13 @@ export default class DatabasePostgresCreate extends Command { } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !name || !databaseName || !appName || !databasePassword) { + if (!projectId || !environmentId || !name || !databaseName || !appName || !databasePassword) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -85,9 +94,32 @@ export default class DatabasePostgresCreate extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } + + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + environmentId = environment.environmentId; + } + if (!name || !databaseName || !appName || !databasePassword) { const dbDetails = await inquirer.prompt([ { @@ -182,6 +214,7 @@ export default class DatabasePostgresCreate extends Command { dockerImage, appName, projectId, + environmentId, }, }, { diff --git a/src/commands/database/postgres/delete.ts b/src/commands/database/postgres/delete.ts index e6d6771..339d66c 100644 --- a/src/commands/database/postgres/delete.ts +++ b/src/commands/database/postgres/delete.ts @@ -4,7 +4,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { readAuthConfig } from "../../../utils/utils.js"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; export default class DatabasePostgresDelete extends Command { static description = "Delete a PostgreSQL database from a project."; @@ -20,6 +20,11 @@ export default class DatabasePostgresDelete extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), postgresId: Flags.string({ char: "d", description: "ID of the PostgreSQL database", @@ -35,13 +40,17 @@ export default class DatabasePostgresDelete extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabasePostgresDelete); - let { projectId, postgresId } = flags; + let { projectId, environmentId, postgresId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !postgresId) { + if (!projectId || !environmentId || !postgresId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const answers = await inquirer.prompt([ { @@ -54,20 +63,44 @@ export default class DatabasePostgresDelete extends Command { type: "list", }, ]); + selectedProject = projects.find(p => p.projectId === answers.selectedProject); projectId = answers.selectedProject; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (!projectSelected.postgres || projectSelected.postgres.length === 0) { - this.error(chalk.yellow("No PostgreSQL instances found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar PostgreSQL del environment if (!postgresId) { + if (!selectedEnvironment?.postgres || selectedEnvironment.postgres.length === 0) { + this.error(chalk.yellow("No PostgreSQL instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ { - // @ts-ignore - choices: projectSelected.postgres.map((db) => ({ + choices: selectedEnvironment.postgres.map((db: Database) => ({ name: db.name, value: db.postgresId, })), diff --git a/src/commands/database/postgres/deploy.ts b/src/commands/database/postgres/deploy.ts index 747c040..490994d 100644 --- a/src/commands/database/postgres/deploy.ts +++ b/src/commands/database/postgres/deploy.ts @@ -1,7 +1,7 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; import chalk from "chalk"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; import type { Answers } from "../../app/create.js"; import axios from "axios"; @@ -17,6 +17,11 @@ export default class DatabasePostgresDeploy extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), postgresId: Flags.string({ char: "d", description: "ID of the PostgreSQL instance to deploy", @@ -32,13 +37,17 @@ export default class DatabasePostgresDeploy extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabasePostgresDeploy); - let { projectId, postgresId } = flags; + let { projectId, environmentId, postgresId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !postgresId) { + if (!projectId || !environmentId || !postgresId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -51,20 +60,44 @@ export default class DatabasePostgresDeploy extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.postgres.length === 0) { - this.error(chalk.yellow("No PostgreSQL instances found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar PostgreSQL del environment if (!postgresId) { + if (!selectedEnvironment?.postgres || selectedEnvironment.postgres.length === 0) { + this.error(chalk.yellow("No PostgreSQL instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ { - // @ts-ignore - choices: projectSelected.postgres.map((db) => ({ + choices: selectedEnvironment.postgres.map((db: Database) => ({ name: db.name, value: db.postgresId, })), diff --git a/src/commands/database/postgres/stop.ts b/src/commands/database/postgres/stop.ts index ce6aa56..920c56b 100644 --- a/src/commands/database/postgres/stop.ts +++ b/src/commands/database/postgres/stop.ts @@ -1,7 +1,7 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; import chalk from "chalk"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; import type { Answers } from "../../app/create.js"; import axios from "axios"; @@ -17,6 +17,11 @@ export default class DatabasePostgresStop extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), postgresId: Flags.string({ char: "d", description: "ID of the PostgreSQL instance to stop", @@ -32,13 +37,17 @@ export default class DatabasePostgresStop extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabasePostgresStop); - let { projectId, postgresId } = flags; + let { projectId, environmentId, postgresId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !postgresId) { + if (!projectId || !environmentId || !postgresId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -51,30 +60,54 @@ export default class DatabasePostgresStop extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.postgres.length === 0) { - this.error(chalk.yellow("No PostgreSQL instances found in this project.")); - } + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); + } + // 3. Seleccionar PostgreSQL del environment if (!postgresId) { + if (!selectedEnvironment?.postgres || selectedEnvironment.postgres.length === 0) { + this.error(chalk.yellow("No PostgreSQL instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ - { - // @ts-ignore - choices: projectSelected.postgres.map((db) => ({ - name: db.name, - value: db.postgresId, - })), - message: "Select the PostgreSQL instance to stop:", - name: "selectedDb", - type: "list", - }, - ]); - postgresId = dbAnswers.selectedDb; - } + { + choices: selectedEnvironment.postgres.map((db: Database) => ({ + name: db.name, + value: db.postgresId, + })), + message: "Select the PostgreSQL instance to stop:", + name: "selectedDb", + type: "list", + }, + ]); + postgresId = dbAnswers.selectedDb; + } } // Confirmar si no se especifica --skipConfirm diff --git a/src/commands/database/redis/create.ts b/src/commands/database/redis/create.ts index 10fe3d9..4376b10 100644 --- a/src/commands/database/redis/create.ts +++ b/src/commands/database/redis/create.ts @@ -4,7 +4,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { slugify } from "../../../utils/slug.js"; import { readAuthConfig } from "../../../utils/utils.js"; -import { getProjects } from "../../../utils/shared.js"; +import { getProjects, type Database } from "../../../utils/shared.js"; import type { Answers } from "../../app/create.js"; export default class DatabaseRedisCreate extends Command { @@ -18,6 +18,11 @@ export default class DatabaseRedisCreate extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), name: Flags.string({ char: "n", description: "Instance name", @@ -60,10 +65,13 @@ export default class DatabaseRedisCreate extends Command { } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !name || !appName || !databasePassword) { + if (!projectId || !environmentId || !name || !appName || !databasePassword) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -76,7 +84,30 @@ export default class DatabaseRedisCreate extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); + } + + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } + + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + environmentId = environment.environmentId; } if (!name || !appName || !databasePassword) { @@ -155,6 +186,7 @@ export default class DatabaseRedisCreate extends Command { dockerImage, appName, projectId, + environmentId, }, }, { diff --git a/src/commands/database/redis/delete.ts b/src/commands/database/redis/delete.ts index f85ce51..c1bba51 100644 --- a/src/commands/database/redis/delete.ts +++ b/src/commands/database/redis/delete.ts @@ -1,7 +1,7 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; import chalk from "chalk"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; import type { Answers } from "../../app/create.js"; import axios from "axios"; @@ -20,6 +20,11 @@ export default class DatabaseRedisDelete extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), redisId: Flags.string({ char: "r", description: "ID of the Redis instance to delete", @@ -35,13 +40,17 @@ export default class DatabaseRedisDelete extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseRedisDelete); - let { projectId, redisId } = flags; + let { projectId, environmentId, redisId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !redisId) { + if (!projectId || !environmentId || !redisId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const answers = await inquirer.prompt([ { @@ -54,20 +63,44 @@ export default class DatabaseRedisDelete extends Command { type: "list", }, ]); + selectedProject = projects.find(p => p.projectId === answers.selectedProject); projectId = answers.selectedProject; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (!projectSelected.redis || projectSelected.redis.length === 0) { - this.error(chalk.yellow("No Redis instances found in this project.")); + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); } + // 3. Seleccionar Redis del environment if (!redisId) { + if (!selectedEnvironment?.redis || selectedEnvironment.redis.length === 0) { + this.error(chalk.yellow("No Redis instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ { - // @ts-ignore - choices: projectSelected.redis.map((db) => ({ + choices: selectedEnvironment.redis.map((db: Database) => ({ name: db.name, value: db.redisId, })), diff --git a/src/commands/database/redis/deploy.ts b/src/commands/database/redis/deploy.ts index 822ba52..5cd7d7b 100644 --- a/src/commands/database/redis/deploy.ts +++ b/src/commands/database/redis/deploy.ts @@ -1,7 +1,7 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; import chalk from "chalk"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; import type { Answers } from "../../app/create.js"; import axios from "axios"; @@ -17,6 +17,11 @@ export default class DatabaseRedisDeploy extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), redisId: Flags.string({ char: "r", description: "ID of the Redis instance to deploy", @@ -32,13 +37,17 @@ export default class DatabaseRedisDeploy extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseRedisDeploy); - let { projectId, redisId } = flags; + let { projectId, environmentId, redisId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !redisId) { + if (!projectId || !environmentId || !redisId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -51,30 +60,54 @@ export default class DatabaseRedisDeploy extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.redis.length === 0) { - this.error(chalk.yellow("No Redis instances found in this project.")); - } + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); + } + // 3. Seleccionar Redis del environment if (!redisId) { + if (!selectedEnvironment?.redis || selectedEnvironment.redis.length === 0) { + this.error(chalk.yellow("No Redis instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ - { - // @ts-ignore - choices: projectSelected.redis.map((db) => ({ - name: db.name, - value: db.redisId, - })), - message: "Select the Redis instance to deploy:", - name: "selectedDb", - type: "list", - }, - ]); - redisId = dbAnswers.selectedDb; - } + { + choices: selectedEnvironment.redis.map((db: Database) => ({ + name: db.name, + value: db.redisId, + })), + message: "Select the Redis instance to deploy:", + name: "selectedDb", + type: "list", + }, + ]); + redisId = dbAnswers.selectedDb; + } } // Confirmar si no se especifica --skipConfirm diff --git a/src/commands/database/redis/stop.ts b/src/commands/database/redis/stop.ts index 3a9b058..a71a2b5 100644 --- a/src/commands/database/redis/stop.ts +++ b/src/commands/database/redis/stop.ts @@ -1,7 +1,7 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; import chalk from "chalk"; -import { getProject, getProjects } from "../../../utils/shared.js"; +import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; import type { Answers } from "../../app/create.js"; import axios from "axios"; @@ -17,6 +17,11 @@ export default class DatabaseRedisStop extends Command { description: "ID of the project", required: false, }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment", + required: false, + }), redisId: Flags.string({ char: "r", description: "ID of the Redis instance to stop", @@ -32,13 +37,17 @@ export default class DatabaseRedisStop extends Command { public async run(): Promise { const auth = await readAuthConfig(this); const { flags } = await this.parse(DatabaseRedisStop); - let { projectId, redisId } = flags; + let { projectId, environmentId, redisId } = flags; // Modo interactivo si no se proporcionan los flags necesarios - if (!projectId || !redisId) { + if (!projectId || !environmentId || !redisId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); + let selectedProject; + let selectedEnvironment; + + // 1. Seleccionar proyecto if (!projectId) { const { project } = await inquirer.prompt([ { @@ -51,30 +60,54 @@ export default class DatabaseRedisStop extends Command { type: "list", }, ]); + selectedProject = project; projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); } - const projectSelected = await getProject(projectId, auth, this); + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } - if (projectSelected.redis.length === 0) { - this.error(chalk.yellow("No Redis instances found in this project.")); - } + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + selectedEnvironment = environment; + environmentId = environment.environmentId; + } else { + selectedEnvironment = selectedProject?.environments?.find(e => e.environmentId === environmentId); + } + // 3. Seleccionar Redis del environment if (!redisId) { + if (!selectedEnvironment?.redis || selectedEnvironment.redis.length === 0) { + this.error(chalk.yellow("No Redis instances found in this environment.")); + } + const dbAnswers = await inquirer.prompt([ - { - // @ts-ignore - choices: projectSelected.redis.map((db) => ({ - name: db.name, - value: db.redisId, - })), - message: "Select the Redis instance to stop:", - name: "selectedDb", - type: "list", - }, - ]); - redisId = dbAnswers.selectedDb; - } + { + choices: selectedEnvironment.redis.map((db: Database) => ({ + name: db.name, + value: db.redisId, + })), + message: "Select the Redis instance to stop:", + name: "selectedDb", + type: "list", + }, + ]); + redisId = dbAnswers.selectedDb; + } } // Confirmar si no se especifica --skipConfirm diff --git a/src/commands/environment/create.ts b/src/commands/environment/create.ts new file mode 100644 index 0000000..761008d --- /dev/null +++ b/src/commands/environment/create.ts @@ -0,0 +1,131 @@ +import { Command, Flags } from "@oclif/core"; +import axios from "axios"; +import chalk from "chalk"; +import inquirer from "inquirer"; + +import { getProjects } from "../../utils/shared.js"; +import { readAuthConfig } from "../../utils/utils.js"; +import type { Answers } from "../app/create.js"; + +export default class EnvironmentCreate extends Command { + static description = "Create a new environment within a project."; + + static examples = ["$ <%= config.bin %> environment create"]; + + static flags = { + projectId: Flags.string({ + char: "p", + description: "ID of the project", + required: false, + }), + name: Flags.string({ + char: "n", + description: "Environment name", + required: false, + }), + description: Flags.string({ + char: "d", + description: "Environment description", + required: false, + }), + skipConfirm: Flags.boolean({ + char: "y", + description: "Skip confirmation prompt", + default: false, + }), + }; + + public async run(): Promise { + const auth = await readAuthConfig(this); + const { flags } = await this.parse(EnvironmentCreate); + let { projectId, name, description } = flags; + + // Modo interactivo si no se proporcionan los flags necesarios + if (!projectId || !name) { + console.log(chalk.blue.bold("\n Listing all Projects \n")); + const projects = await getProjects(auth, this); + + // 1. Seleccionar proyecto + if (!projectId) { + const { project } = await inquirer.prompt([ + { + choices: projects.map((project) => ({ + name: project.name, + value: project, + })), + message: "Select a project to create the environment in:", + name: "project", + type: "list", + }, + ]); + projectId = project.projectId; + } + + // 2. Ingresar detalles del environment + if (!name) { + const envDetails = await inquirer.prompt([ + { + message: "Enter the environment name:", + name: "name", + type: "input", + validate: (input) => (input ? true : "Environment name is required"), + default: name, + }, + { + message: "Enter the environment description (optional):", + name: "description", + type: "input", + default: description, + }, + ]); + + name = envDetails.name; + description = envDetails.description; + } + } + + // Confirmar si no se especifica --skipConfirm + if (!flags.skipConfirm) { + const confirm = await inquirer.prompt([ + { + type: 'confirm', + name: 'proceed', + message: 'Do you want to create this environment?', + default: false, + }, + ]); + + if (!confirm.proceed) { + this.error(chalk.yellow("Environment creation cancelled.")); + return; + } + } + + try { + const response = await axios.post( + `${auth.url}/api/trpc/environment.create`, + { + json: { + name, + description, + projectId, + }, + }, + { + headers: { + "x-api-key": auth.token, + "Content-Type": "application/json", + }, + }, + ); + + if (!response.data.result.data.json) { + this.error(chalk.red("Error creating environment")); + } + + this.log(chalk.green(`Environment '${name}' created successfully.`)); + } catch (error: any) { + this.error(chalk.red(`Error creating environment: ${error.message}`)); + } + } +} diff --git a/src/commands/environment/delete.ts b/src/commands/environment/delete.ts new file mode 100644 index 0000000..8417ac1 --- /dev/null +++ b/src/commands/environment/delete.ts @@ -0,0 +1,129 @@ +import { Command, Flags } from "@oclif/core"; +import axios from "axios"; +import chalk from "chalk"; +import inquirer from "inquirer"; + +import { getProjects } from "../../utils/shared.js"; +import { readAuthConfig } from "../../utils/utils.js"; +import type { Answers } from "../app/create.js"; + +export default class EnvironmentDelete extends Command { + static description = "Delete an environment from a project."; + + static examples = [ + "$ <%= config.bin %> environment delete", + "$ <%= config.bin %> environment delete -p ", + ]; + + static flags = { + projectId: Flags.string({ + char: "p", + description: "ID of the project", + required: false, + }), + environmentId: Flags.string({ + char: "e", + description: "ID of the environment to delete", + required: false, + }), + skipConfirm: Flags.boolean({ + char: "y", + description: "Skip confirmation prompt", + default: false, + }), + }; + + public async run(): Promise { + const auth = await readAuthConfig(this); + const { flags } = await this.parse(EnvironmentDelete); + let { projectId, environmentId } = flags; + + // Modo interactivo si no se proporcionan los flags necesarios + if (!projectId || !environmentId) { + console.log(chalk.blue.bold("\n Listing all Projects \n")); + const projects = await getProjects(auth, this); + + let selectedProject; + + // 1. Seleccionar proyecto + if (!projectId) { + const { project } = await inquirer.prompt([ + { + choices: projects.map((project) => ({ + name: project.name, + value: project, + })), + message: "Select a project to delete the environment from:", + name: "project", + type: "list", + }, + ]); + selectedProject = project; + projectId = project.projectId; + } else { + selectedProject = projects.find(p => p.projectId === projectId); + } + + // 2. Seleccionar environment del proyecto + if (!environmentId) { + if (!selectedProject?.environments || selectedProject.environments.length === 0) { + this.error(chalk.yellow("No environments found in this project.")); + } + + const { environment } = await inquirer.prompt([ + { + choices: selectedProject.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment to delete:", + name: "environment", + type: "list", + }, + ]); + environmentId = environment.environmentId; + } + } + + // Confirmar si no se especifica --skipConfirm + if (!flags.skipConfirm) { + const confirmAnswers = await inquirer.prompt([ + { + default: false, + message: "Are you sure you want to delete this environment? This action cannot be undone.", + name: "confirmDelete", + type: "confirm", + }, + ]); + + if (!confirmAnswers.confirmDelete) { + this.error(chalk.yellow("Environment deletion cancelled.")); + } + } + + try { + const response = await axios.post( + `${auth.url}/api/trpc/environment.remove`, + { + json: { + environmentId, + }, + }, + { + headers: { + "x-api-key": auth.token, + "Content-Type": "application/json", + }, + }, + ); + + if (!response.data.result.data.json) { + this.error(chalk.red("Error deleting environment")); + } + + this.log(chalk.green("Environment deleted successfully.")); + } catch (error: any) { + this.error(chalk.red(`Error deleting environment: ${error.message}`)); + } + } +} diff --git a/src/commands/project/info.ts b/src/commands/project/info.ts index 61992eb..b461953 100644 --- a/src/commands/project/info.ts +++ b/src/commands/project/info.ts @@ -3,7 +3,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { readAuthConfig } from "../../utils/utils.js"; -import { getProject, getProjects } from "../../utils/shared.js"; +import { getProjects } from "../../utils/shared.js"; export default class ProjectInfo extends Command { static description = @@ -71,7 +71,13 @@ export default class ProjectInfo extends Command { ); try { - const projectInfo = await getProject(projectId, auth, this); + const projects = await getProjects(auth, this); + const projectInfo = projects.find(p => p.projectId === projectId); + + if (!projectInfo) { + this.error(chalk.red("Project not found.")); + return; + } this.log(chalk.green(`Project Name: ${projectInfo.name}`)); this.log( @@ -79,91 +85,106 @@ export default class ProjectInfo extends Command { `Description: ${projectInfo?.description || "No description"}`, ), ); - this.log( - chalk.green( - `Number of Applications: ${projectInfo.applications.length}`, - ), - ); - this.log( - chalk.green( - `Number of Compose Services: ${projectInfo.compose.length}`, - ), - ); - this.log( - chalk.green( - `Number of MariaDB Databases: ${projectInfo.mariadb.length}`, - ), - ); - this.log( - chalk.green(`Number of MongoDB Databases: ${projectInfo.mongo.length}`), - ); - this.log( - chalk.green(`Number of MySQL Databases: ${projectInfo.mysql.length}`), - ); - this.log( - chalk.green( - `Number of PostgreSQL Databases: ${projectInfo.postgres.length}`, - ), - ); - this.log( - chalk.green(`Number of Redis Databases: ${projectInfo.redis.length}`), - ); - - if (projectInfo.applications.length > 0) { - this.log(chalk.blue("\nApplications:")); - // @ts-ignore - projectInfo.applications.forEach((app, index: number) => { - this.log(` ${index + 1}. ${app.name}`); - }); - } - - if (projectInfo.compose.length > 0) { - this.log(chalk.blue("\nCompose Services:")); - // @ts-ignore - projectInfo.compose.forEach((service, index: number) => { - this.log(` ${index + 1}. ${service.name}`); - }); - } - if (projectInfo.mariadb.length > 0) { - this.log(chalk.blue("\nMariaDB Databases:")); - // @ts-ignore - projectInfo.mariadb.forEach((db, index: number) => { - this.log(` ${index + 1}. ${db.name}`); + // Contar totales de todos los environments + let totalApplications = 0; + let totalCompose = 0; + let totalMariaDB = 0; + let totalMongoDB = 0; + let totalMySQL = 0; + let totalPostgreSQL = 0; + let totalRedis = 0; + + if (projectInfo.environments && projectInfo.environments.length > 0) { + this.log(chalk.green(`Number of Environments: ${projectInfo.environments.length}`)); + + // Mostrar información por environment + projectInfo.environments.forEach((env, envIndex) => { + this.log(chalk.blue(`\nEnvironment ${envIndex + 1}: ${env.name} (${env.description})`)); + + // Contar recursos por environment + const envApps = env.applications?.length || 0; + const envCompose = env.compose?.length || 0; + const envMariaDB = env.mariadb?.length || 0; + const envMongoDB = env.mongo?.length || 0; + const envMySQL = env.mysql?.length || 0; + const envPostgreSQL = env.postgres?.length || 0; + const envRedis = env.redis?.length || 0; + + totalApplications += envApps; + totalCompose += envCompose; + totalMariaDB += envMariaDB; + totalMongoDB += envMongoDB; + totalMySQL += envMySQL; + totalPostgreSQL += envPostgreSQL; + totalRedis += envRedis; + + this.log(` Applications: ${envApps}`); + this.log(` Compose Services: ${envCompose}`); + this.log(` MariaDB: ${envMariaDB}`); + this.log(` MongoDB: ${envMongoDB}`); + this.log(` MySQL: ${envMySQL}`); + this.log(` PostgreSQL: ${envPostgreSQL}`); + this.log(` Redis: ${envRedis}`); + + // Mostrar detalles de applications + if (envApps > 0) { + this.log(chalk.cyan(" Applications:")); + env.applications.forEach((app, index) => { + this.log(` ${index + 1}. ${app.name}`); + }); + } + + // Mostrar detalles de databases + if (envMariaDB > 0) { + this.log(chalk.cyan(" MariaDB Databases:")); + env.mariadb.forEach((db, index) => { + this.log(` ${index + 1}. ${db.name}`); + }); + } + + if (envMongoDB > 0) { + this.log(chalk.cyan(" MongoDB Databases:")); + env.mongo.forEach((db, index) => { + this.log(` ${index + 1}. ${db.name}`); + }); + } + + if (envMySQL > 0) { + this.log(chalk.cyan(" MySQL Databases:")); + env.mysql.forEach((db, index) => { + this.log(` ${index + 1}. ${db.name}`); + }); + } + + if (envPostgreSQL > 0) { + this.log(chalk.cyan(" PostgreSQL Databases:")); + env.postgres.forEach((db, index) => { + this.log(` ${index + 1}. ${db.name}`); + }); + } + + if (envRedis > 0) { + this.log(chalk.cyan(" Redis Databases:")); + env.redis.forEach((db, index) => { + this.log(` ${index + 1}. ${db.name}`); + }); + } }); + } else { + this.log(chalk.yellow("No environments found in this project.")); } - if (projectInfo.mongo.length > 0) { - this.log(chalk.blue("\nMongoDB Databases:")); - // @ts-ignore - projectInfo.mongo.forEach((db, index: number) => { - this.log(` ${index + 1}. ${db.name}`); - }); - } - - if (projectInfo.mysql.length > 0) { - this.log(chalk.blue("\nMySQL Databases:")); - // @ts-ignore - projectInfo.mysql.forEach((db, index: number) => { - this.log(` ${index + 1}. ${db.name}`); - }); - } + // Mostrar totales + this.log(chalk.green.bold("\n📊 Project Totals:")); + this.log(chalk.green(`Total Applications: ${totalApplications}`)); + this.log(chalk.green(`Total Compose Services: ${totalCompose}`)); + this.log(chalk.green(`Total MariaDB Databases: ${totalMariaDB}`)); + this.log(chalk.green(`Total MongoDB Databases: ${totalMongoDB}`)); + this.log(chalk.green(`Total MySQL Databases: ${totalMySQL}`)); + this.log(chalk.green(`Total PostgreSQL Databases: ${totalPostgreSQL}`)); + this.log(chalk.green(`Total Redis Databases: ${totalRedis}`)); - if (projectInfo.postgres.length > 0) { - this.log(chalk.blue("\nPostgreSQL Databases:")); - // @ts-ignore - projectInfo.postgres.forEach((db, index: number) => { - this.log(` ${index + 1}. ${db.name}`); - }); - } - - if (projectInfo.redis.length > 0) { - this.log(chalk.blue("\nRedis Databases:")); - // @ts-ignore - projectInfo.redis.forEach((db, index: number) => { - this.log(` ${index + 1}. ${db.name}`); - }); - } } catch (error) { this.error( // @ts-expect-error diff --git a/src/utils/shared.ts b/src/utils/shared.ts index 9d0f32b..ad7e832 100644 --- a/src/utils/shared.ts +++ b/src/utils/shared.ts @@ -5,11 +5,44 @@ import chalk from "chalk"; import type { AuthConfig } from "./utils.js"; +export type Application = { + applicationId: string; + name: string; + // Add other application properties as needed +}; + +export type Database = { + mariadbId?: string; + mongoId?: string; + mysqlId?: string; + postgresId?: string; + redisId?: string; + name: string; + // Add other database properties as needed +}; + +export type Environment = { + name: string; + environmentId: string; + description: string; + createdAt: string; + env: string; + projectId: string; + applications: Application[]; + mariadb: Database[]; + mongo: Database[]; + mysql: Database[]; + postgres: Database[]; + redis: Database[]; + compose: any[]; +}; + export type Project = { adminId: string; name: string; projectId?: string | undefined; description?: string | undefined; + environments?: Environment[]; }; export const getProjects = async (