From 1f17945495bdc66f74d53cdb0fe9ad489b106e5b Mon Sep 17 00:00:00 2001 From: anudeeps352 Date: Mon, 21 Oct 2024 04:31:38 +0530 Subject: [PATCH] added all secret CLI commands --- apps/cli/src/commands/secret.command.ts | 24 +++--- apps/cli/src/commands/secret/create.secret.ts | 56 ++++++++----- apps/cli/src/commands/secret/delete.secret.ts | 44 +++++++++++ apps/cli/src/commands/secret/get.secret.ts | 75 ++++++++++++++++++ apps/cli/src/commands/secret/list.secret.ts | 56 +++++++++++++ .../src/commands/secret/revisions.secret.ts | 62 +++++++++++++++ .../src/commands/secret/rollback.secret.ts | 79 +++++++++++++++++++ apps/cli/src/commands/secret/update.secret.ts | 2 +- apps/cli/src/index.ts | 2 + 9 files changed, 369 insertions(+), 31 deletions(-) create mode 100644 apps/cli/src/commands/secret/delete.secret.ts create mode 100644 apps/cli/src/commands/secret/get.secret.ts create mode 100644 apps/cli/src/commands/secret/list.secret.ts create mode 100644 apps/cli/src/commands/secret/revisions.secret.ts create mode 100644 apps/cli/src/commands/secret/rollback.secret.ts diff --git a/apps/cli/src/commands/secret.command.ts b/apps/cli/src/commands/secret.command.ts index 471fdf1c..fa8e7b6d 100644 --- a/apps/cli/src/commands/secret.command.ts +++ b/apps/cli/src/commands/secret.command.ts @@ -1,6 +1,13 @@ import BaseCommand from '@/commands/base.command' +import CreateSecret from '@/commands/secret/create.secret' +import DeleteSecret from '@/commands/secret/delete.secret' +import ListSecret from '@/commands/secret/list.secret' +import FetchSecretRevisions from '@/commands/secret/revisions.secret' +import UpdateSecret from '@/commands/secret/update.secret' +import RollbackSecret from '@/commands/secret/rollback.secret' +import GetSecret from '@/commands/secret/get.secret' -export default class WorkspaceCommand extends BaseCommand { +export default class SecretCommand extends BaseCommand { getName(): string { return 'secret' } @@ -11,14 +18,13 @@ export default class WorkspaceCommand extends BaseCommand { getSubCommands(): BaseCommand[] { return [ - new CreateWorkspace(), //change these - new DeleteWorkspace(), - new ExportWorkspace(), - new GetWorkspace(), - new ListWorkspace(), - new SearchWorkspace(), - new UpdateWorkspace(), - new WorkspaceRoleCommand() + new CreateSecret(), + new DeleteSecret(), + new GetSecret(), + new ListSecret(), + new FetchSecretRevisions(), + new UpdateSecret(), + new RollbackSecret() ] } } diff --git a/apps/cli/src/commands/secret/create.secret.ts b/apps/cli/src/commands/secret/create.secret.ts index 2f5e160b..b6745fb0 100644 --- a/apps/cli/src/commands/secret/create.secret.ts +++ b/apps/cli/src/commands/secret/create.secret.ts @@ -1,11 +1,11 @@ -import BaseCommand from '../base.command' +import BaseCommand from '@/commands/base.command' import { text } from '@clack/prompts' import ControllerInstance from '@/util/controller-instance' import { Logger } from '@/util/logger' import { - CommandActionData, - CommandArgument, - CommandOption + type CommandActionData, + type CommandArgument, + type CommandOption } from '@/types/command/command.types' export default class CreateSecret extends BaseCommand { @@ -25,28 +25,30 @@ export default class CreateSecret extends BaseCommand { } ] } + getOptions(): CommandOption[] { return [ { - short: 'n', + short: '-n', long: '--name ', description: 'Name of the secret. Must be unique across the project' }, { - short: 'd', + short: '-d', long: '--note ', description: 'A note describing the usage of the secret.' }, { - short: 'r', + short: '-r', long: '--rotate-after ', description: ' The duration in days after which the value of the secret should be rotated. Accepted values are `24`, `168`, `720`, `8769` and `never`. Defaults to `never`.' }, { - short: 'e', + short: '-e', long: '--entries [entries...]', - description: 'An array of values for the secret.' + description: + 'An array of key-value pair (value and environmentSlug) for the secret.' } ] } @@ -63,10 +65,11 @@ export default class CreateSecret extends BaseCommand { const { data, error, success } = await ControllerInstance.getInstance().secretController.createSecret( { - name, //doubtful as to whether to change it to since there - note, //are optional values present + name, + note, rotateAfter, - entries + entries, + projectSlug }, this.headers ) @@ -84,15 +87,10 @@ export default class CreateSecret extends BaseCommand { name: string note?: string rotateAfter?: '24' | '168' | '720' | '8760' | 'never' - entries: [ - { - value: string - environmentSlug: string - } - ] + entries: Array<{ value: string; environmentSlug: string }> }> { - let { name, note } = options - const { rotateAfter, entries } = options // check what fields can be updated + let { name, note, rotateAfter } = options + const { entries } = options if (!name) { name = await text({ @@ -101,15 +99,31 @@ export default class CreateSecret extends BaseCommand { }) } + if (!entries) { + throw new Error('Entries is required') + } + if (!note) { note = name } + const parsedEntries = entries.map((entry) => { + const entryObj: { value: string; environmentSlug: string } = { + value: '', + environmentSlug: '' + } + entry.split(' ').forEach((pair) => { + const [key, value] = pair.split('=') + entryObj[key] = value + }) + return entryObj + }) + return { name, note, rotateAfter, - entries + entries: parsedEntries } } } diff --git a/apps/cli/src/commands/secret/delete.secret.ts b/apps/cli/src/commands/secret/delete.secret.ts new file mode 100644 index 00000000..1e709e00 --- /dev/null +++ b/apps/cli/src/commands/secret/delete.secret.ts @@ -0,0 +1,44 @@ +import type { + CommandActionData, + CommandArgument +} from '@/types/command/command.types' +import BaseCommand from '@/commands/base.command' +import { Logger } from '@/util/logger' +import ControllerInstance from '@/util/controller-instance' + +export default class DeleteSecret extends BaseCommand { + getName(): string { + return 'delete' + } + + getDescription(): string { + return 'Deletes a secret' + } + + getArguments(): CommandArgument[] { + return [ + { + name: '', + description: 'Slug of the secret that you want to delete.' + } + ] + } + + async action({ args }: CommandActionData): Promise { + const [secretSlug] = args + + const { error, success } = + await ControllerInstance.getInstance().secretController.deleteSecret( + { + secretSlug + }, + this.headers + ) + + if (success) { + Logger.info(`Secret ${secretSlug} deleted successfully!`) + } else { + Logger.error(`Failed to delete secret: ${error.message}`) + } + } +} diff --git a/apps/cli/src/commands/secret/get.secret.ts b/apps/cli/src/commands/secret/get.secret.ts new file mode 100644 index 00000000..b015ed23 --- /dev/null +++ b/apps/cli/src/commands/secret/get.secret.ts @@ -0,0 +1,75 @@ +import type { + CommandActionData, + CommandArgument, + CommandOption +} from '@/types/command/command.types' +import BaseCommand from '@/commands/base.command' +import ControllerInstance from '@/util/controller-instance' +import { Logger } from '@/util/logger' + +export default class GetSecret extends BaseCommand { + getName(): string { + return 'get' + } + + getDescription(): string { + return 'Get all secrets under a project' + } + + getArguments(): CommandArgument[] { + return [ + { + name: '', + description: 'Slug of the project whose secrets you want.' + } + ] + } + + getOptions(): CommandOption[] { + return [ + { + short: '-d', + long: '--decrypt-value', + description: + 'Set this to true if the project contains the private key. If set to true, the values of the secret will be in plaintext format' + } + ] + } + + async action({ args, options }: CommandActionData): Promise { + const [projectSlug] = args + const { decryptValue } = await this.parseInput(options) + const { data, error, success } = + await ControllerInstance.getInstance().secretController.getAllSecretsOfProject( + { + projectSlug, + decryptValue + }, + this.headers + ) + + if (success) { + const secrets = data.items + + if (secrets.length > 0) { + data.items.forEach((item: any) => { + const secret = item.secret + Logger.info(`- ${secret.name} (${secret.slug})`) + }) + } else { + Logger.info('No secrets found') + } + } else { + Logger.error(`Failed fetching secrets: ${error.message}`) + } + } + + private async parseInput(options: CommandActionData['options']): Promise<{ + decryptValue: boolean + }> { + const { decryptValue = false } = options // defaults to false + return { + decryptValue + } + } +} diff --git a/apps/cli/src/commands/secret/list.secret.ts b/apps/cli/src/commands/secret/list.secret.ts new file mode 100644 index 00000000..cfb78992 --- /dev/null +++ b/apps/cli/src/commands/secret/list.secret.ts @@ -0,0 +1,56 @@ +import type { + CommandActionData, + CommandArgument +} from '@/types/command/command.types' +import BaseCommand from '@/commands/base.command' +import ControllerInstance from '@/util/controller-instance' +import { Logger } from '@/util/logger' + +export default class ListSecret extends BaseCommand { + getName(): string { + return 'list' + } + + getDescription(): string { + return 'List all secrets under a project and environment' + } + + getArguments(): CommandArgument[] { + return [ + { + name: '', + description: 'Slug of the project whose secrets you want.' + }, + { + name: '', + description: 'Slug of the environment whose secrets you want.' + } + ] + } + + async action({ args }: CommandActionData): Promise { + const [projectSlug, environmentSlug] = args + const { data, error, success } = + await ControllerInstance.getInstance().secretController.getAllSecretsOfEnvironment( + { + projectSlug, + environmentSlug + }, + this.headers + ) + + if (success) { + console.log(data) + const secrets = data + if (secrets.length > 0) { + data.forEach((secret: any) => { + Logger.info(`- ${secret.name} (${secret.value})`) + }) + } else { + Logger.info('No secrets found') + } + } else { + Logger.error(`Failed fetching secrets: ${error.message}`) + } + } +} diff --git a/apps/cli/src/commands/secret/revisions.secret.ts b/apps/cli/src/commands/secret/revisions.secret.ts new file mode 100644 index 00000000..9a031b63 --- /dev/null +++ b/apps/cli/src/commands/secret/revisions.secret.ts @@ -0,0 +1,62 @@ +import type { + CommandActionData, + CommandArgument +} from '@/types/command/command.types' +import BaseCommand from '@/commands/base.command' +import ControllerInstance from '@/util/controller-instance' +import { Logger } from '@/util/logger' + +export default class FetchSecretRevisions extends BaseCommand { + getName(): string { + return 'revisions' + } + + getDescription(): string { + return 'Fetch all revisions of a secret' + } + + getArguments(): CommandArgument[] { + return [ + { + name: '', + description: 'Slug of the secret whose revisions you want.' + }, + { + name: '', + description: 'Environment slug of the secret whose revisions you want.' + } + ] + } + + async action({ args }: CommandActionData): Promise { + const [secretSlug, environmentSlug] = args + + const { data, error, success } = + await ControllerInstance.getInstance().secretController.getRevisionsOfSecret( + { + secretSlug, + environmentSlug + }, + this.headers + ) + + if (success) { + const revisions = data.items + if (revisions.length > 0) { + data.items.forEach((revision: any) => { + Logger.info(`Id ${revision.id}`) + Logger.info(`value ${revision.value}`) + Logger.info(`version ${revision.version}`) + Logger.info(`secretID ${revision.secretId}`) + Logger.info(`Created On ${revision.createdOn}`) + Logger.info(`Created By Id ${revision.createdById}`) + Logger.info(`environmentId ${revision.environmentId}`) + }) + } else { + Logger.info('No revisions found') + } + } else { + Logger.error(`Failed fetching revisions: ${error.message}`) + } + } +} diff --git a/apps/cli/src/commands/secret/rollback.secret.ts b/apps/cli/src/commands/secret/rollback.secret.ts new file mode 100644 index 00000000..6892c6f1 --- /dev/null +++ b/apps/cli/src/commands/secret/rollback.secret.ts @@ -0,0 +1,79 @@ +import type { + CommandActionData, + CommandArgument, + CommandOption +} from '@/types/command/command.types' +import BaseCommand from '@/commands/base.command' +import { Logger } from '@/util/logger' +import ControllerInstance from '@/util/controller-instance' + +export default class RollbackSecret extends BaseCommand { + getName(): string { + return 'rollback' + } + + getDescription(): string { + return 'Rollbacks a secret' + } + + getArguments(): CommandArgument[] { + return [ + { + name: '', + description: 'Slug of the secret that you want to rollback' + } + ] + } + + getOptions(): CommandOption[] { + return [ + { + short: '-v', + long: '--version ', + description: 'Version of the secret to which you want to rollback' + }, + { + short: '-e', + long: '--environmentSlug ', + description: + 'Slug of the environment of the secret to which you want to rollback' + } + ] + } + + async action({ args, options }: CommandActionData): Promise { + const [secretSlug] = args + const { environmentSlug, version } = await this.parseInput(options) + const { data, error, success } = + await ControllerInstance.getInstance().secretController.rollbackSecret( + { + environmentSlug, + version, + secretSlug + }, + this.headers + ) + + if (success) { + Logger.info(`Secret ${data.name} (${data.slug}) updated successfully!`) + Logger.info(`Created at ${data.createdAt}`) + Logger.info(`Updated at ${data.updatedAt}`) + Logger.info(`Note: ${data.note}`) + Logger.info(`rotateAfter: ${data.rotateAfter}`) + } else { + Logger.error(`Failed to update secret: ${error.message}`) + } + } + + private async parseInput(options: CommandActionData['options']): Promise<{ + environmentSlug: string + version: string + }> { + const { environmentSlug, version } = options + + return { + environmentSlug, + version + } + } +} diff --git a/apps/cli/src/commands/secret/update.secret.ts b/apps/cli/src/commands/secret/update.secret.ts index 0949e6e6..d31b78e7 100644 --- a/apps/cli/src/commands/secret/update.secret.ts +++ b/apps/cli/src/commands/secret/update.secret.ts @@ -3,7 +3,7 @@ import type { CommandArgument, CommandOption } from '@/types/command/command.types' -import BaseCommand from '../base.command' +import BaseCommand from '@/commands/base.command' import { Logger } from '@/util/logger' import ControllerInstance from '@/util/controller-instance' diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index 793765a3..3f908dd0 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -7,6 +7,7 @@ import EnvironmentCommand from './commands/environment.command' import WorkspaceCommand from '@/commands/workspace.command' import ScanCommand from '@/commands/scan.command' import ProjectCommand from './commands/project.command' +import SecretCommand from './commands/secret.command' const program = new Command() @@ -21,6 +22,7 @@ const COMMANDS: BaseCommand[] = [ new WorkspaceCommand(), new ProjectCommand(), new EnvironmentCommand(), + new SecretCommand(), new ScanCommand() ]