mirror of
https://github.com/Dokploy/cli.git
synced 2026-06-15 20:25:22 +02:00
Merge pull request #36 from Dokploy/feat/add-support-for-all-endpoints
Feat/add support for all endpoints
This commit is contained in:
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
DOKPLOY_URL=""
|
||||
DOKPLOY_API_KEY=""
|
||||
@@ -1 +0,0 @@
|
||||
/dist
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": [ "prettier"]
|
||||
}
|
||||
52
.github/workflows/onPushToMain.yml
vendored
52
.github/workflows/onPushToMain.yml
vendored
@@ -1,56 +1,62 @@
|
||||
# test
|
||||
name: version, tag and github release
|
||||
name: release and publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
COREPACK_ENABLE_STRICT: 0
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
cache: pnpm
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- run: pnpm install --ignore-scripts
|
||||
- run: pnpm run build
|
||||
|
||||
- name: Check if version already exists
|
||||
id: version-check
|
||||
run: |
|
||||
package_version=$(node -p "require('./package.json').version")
|
||||
exists=$(gh api repos/${{ github.repository }}/releases/tags/v$package_version >/dev/null 2>&1 && echo "true" || echo "")
|
||||
|
||||
if [ -n "$exists" ];
|
||||
then
|
||||
if [ -n "$exists" ]; then
|
||||
echo "Version v$package_version already exists"
|
||||
echo "::warning file=package.json,line=1::Version v$package_version already exists - no release will be created. If you want to create a new release, please update the version in package.json and push again."
|
||||
echo "skipped=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Version v$package_version does not exist. Creating release..."
|
||||
echo "Version v$package_version does not exist"
|
||||
echo "skipped=false" >> $GITHUB_OUTPUT
|
||||
echo "tag=v$package_version" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
- name: Setup git
|
||||
if: ${{ steps.version-check.outputs.skipped == 'false' }}
|
||||
run: |
|
||||
git config --global user.email ${{ secrets.GH_EMAIL }}
|
||||
git config --global user.name ${{ secrets.GH_USERNAME }}
|
||||
- name: Generate oclif README
|
||||
if: ${{ steps.version-check.outputs.skipped == 'false' }}
|
||||
id: oclif-readme
|
||||
run: |
|
||||
pnpm install
|
||||
pnpm exec oclif readme
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
git add .
|
||||
git commit -am "chore: update README.md"
|
||||
git push -u origin ${{ github.ref_name }}
|
||||
fi
|
||||
|
||||
- name: Create Github Release
|
||||
if: steps.version-check.outputs.skipped == 'false'
|
||||
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5
|
||||
if: ${{ steps.version-check.outputs.skipped == 'false' }}
|
||||
with:
|
||||
name: ${{ steps.version-check.outputs.tag }}
|
||||
tag: ${{ steps.version-check.outputs.tag }}
|
||||
commit: ${{ github.ref_name }}
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
skipIfReleaseExists: true
|
||||
|
||||
- name: Publish to npm
|
||||
if: steps.version-check.outputs.skipped == 'false'
|
||||
run: pnpm publish --no-git-checks
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
18
.github/workflows/onRelease.yml
vendored
18
.github/workflows/onRelease.yml
vendored
@@ -1,18 +0,0 @@
|
||||
name: publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: pnpm install
|
||||
- uses: JS-DevTools/npm-publish@19c28f1ef146469e409470805ea4279d47c3d35c
|
||||
with:
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
16
.github/workflows/test.yml
vendored
16
.github/workflows/test.yml
vendored
@@ -1,22 +1,30 @@
|
||||
name: tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
COREPACK_ENABLE_STRICT: 0
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
os: ['ubuntu-latest', 'windows-latest']
|
||||
node_version: [lts/-1, lts/*, latest]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node_version: [lts/*, latest]
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
version: latest
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: pnpm
|
||||
- run: pnpm install
|
||||
- run: pnpm install --ignore-scripts
|
||||
- run: pnpm run build
|
||||
- run: pnpm run test
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,5 +11,4 @@ oclif.manifest.json
|
||||
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
|
||||
|
||||
.env
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"require": [
|
||||
"ts-node/register"
|
||||
],
|
||||
"watch-extensions": [
|
||||
"ts"
|
||||
],
|
||||
"recursive": true,
|
||||
"reporter": "spec",
|
||||
"timeout": 60000,
|
||||
"node-option": [
|
||||
"loader=ts-node/esm",
|
||||
"experimental-specifier-resolution=node"
|
||||
]
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
"@oclif/prettier-config"
|
||||
124
CONTRIBUTING.md
124
CONTRIBUTING.md
@@ -1,113 +1,63 @@
|
||||
|
||||
|
||||
# Contributing
|
||||
|
||||
Hey, thanks for your interest in contributing to Dokploy CLI! We appreciate your help and taking your time to contribute.
|
||||
|
||||
|
||||
Before you start, please first discuss the feature/bug you want to add with the owners and comunity via github issues.
|
||||
|
||||
We have a few guidelines to follow when contributing to this project:
|
||||
|
||||
- [Commit Convention](#commit-convention)
|
||||
- [Setup](#setup)
|
||||
- [Development](#development)
|
||||
- [Build](#build)
|
||||
- [Pull Request](#pull-request)
|
||||
|
||||
## Commit Convention
|
||||
|
||||
Before you craete a Pull Request, please make sure your commit message follows the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
|
||||
|
||||
### Commit Message Format
|
||||
```
|
||||
<type>[optional scope]: <description>
|
||||
|
||||
[optional body]
|
||||
|
||||
[optional footer(s)]
|
||||
```
|
||||
|
||||
#### Type
|
||||
Must be one of the following:
|
||||
|
||||
* **feat**: A new feature
|
||||
* **fix**: A bug fix
|
||||
* **docs**: Documentation only changes
|
||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **perf**: A code change that improves performance
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
||||
* **chore**: Other changes that don't modify `src` or `test` files
|
||||
* **revert**: Reverts a previous commit
|
||||
|
||||
Example:
|
||||
```
|
||||
feat: add new feature
|
||||
```
|
||||
|
||||
|
||||
Thanks for your interest in contributing to Dokploy CLI!
|
||||
|
||||
Before you start, please discuss the feature/bug via [GitHub issues](https://github.com/Dokploy/cli/issues).
|
||||
|
||||
## Setup
|
||||
|
||||
Before you start, please make the clone based on the `main` branch.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Dokploy/cli.git
|
||||
cd cli
|
||||
pnpm install
|
||||
```
|
||||
|
||||
Create a `.env` file with your credentials:
|
||||
|
||||
```env
|
||||
DOKPLOY_URL="https://your-server.dokploy.com"
|
||||
DOKPLOY_API_KEY="YOUR_API_KEY"
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
First step is to authenticate, you can connect to a dokploy localhost or a remote dokploy server.
|
||||
|
||||
Authenticate
|
||||
|
||||
```bash
|
||||
./bin/dev.js authenticate
|
||||
```
|
||||
# Run in dev mode
|
||||
pnpm run dev -- project all
|
||||
|
||||
Let's take the example to create a new command for application called `start`.
|
||||
# Regenerate commands from OpenAPI spec
|
||||
pnpm run generate
|
||||
|
||||
You can use the generators from OCLIF to create a new command.
|
||||
|
||||
```bash
|
||||
oclif generate command application:start
|
||||
```
|
||||
|
||||
To run the command, you can use the following command:
|
||||
|
||||
```bash
|
||||
./bin/dev.js application:start or ./bin/dev.js start
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
# Build
|
||||
pnpm run build
|
||||
|
||||
# Lint & format
|
||||
pnpm run lint
|
||||
```
|
||||
|
||||
## Publish
|
||||
### Updating commands
|
||||
|
||||
```bash
|
||||
pnpm run publish
|
||||
Commands in `src/generated/commands.ts` are auto-generated from `openapi.json`. Never edit that file manually. To update:
|
||||
|
||||
1. Replace `openapi.json` with the latest spec from the [Dokploy repo](https://github.com/Dokploy/dokploy)
|
||||
2. Run `pnpm run generate`
|
||||
|
||||
## Commit convention
|
||||
|
||||
Follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/):
|
||||
|
||||
```
|
||||
feat: add new feature
|
||||
fix: resolve bug
|
||||
docs: update readme
|
||||
chore: bump version
|
||||
```
|
||||
|
||||
## Pull requests
|
||||
|
||||
## Pull Request
|
||||
|
||||
- The `main` branch is the source of truth and should always reflect the latest stable release.
|
||||
- Create a new branch for each feature or bug fix.
|
||||
- Make sure to add tests for your changes.
|
||||
- Make sure to update the documentation for any changes Go to the [docs.dokploy.com](https://docs.dokploy.com) website to see the changes.
|
||||
- When creating a pull request, please provide a clear and concise description of the changes made.
|
||||
- If you include a video or screenshot, would be awesome so we can see the changes in action.
|
||||
- If your pull request fixes an open issue, please reference the issue in the pull request description.
|
||||
- Once your pull request is merged, you will be automatically added as a contributor to the project.
|
||||
- Branch from `main`
|
||||
- Provide a clear description of your changes
|
||||
- Reference any related issues
|
||||
- Include a screenshot/video if applicable
|
||||
|
||||
Thank you for your contribution!
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Mauricio Siu
|
||||
Copyright (c) 2026 Dokploy Technologies, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
|
||||
node --loader ts-node/esm --no-warnings=ExperimentalWarning "%~dp0\dev" %*
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env -S node --loader ts-node/esm --no-warnings=ExperimentalWarning
|
||||
|
||||
// eslint-disable-next-line n/shebang
|
||||
import {execute} from '@oclif/core'
|
||||
|
||||
await execute({development: true, dir: import.meta.url})
|
||||
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
|
||||
node "%~dp0\run" %*
|
||||
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import {execute} from '@oclif/core'
|
||||
|
||||
await execute({dir: import.meta.url})
|
||||
import "../dist/index.js";
|
||||
|
||||
42
biome.json
Normal file
42
biome.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": true,
|
||||
"includes": ["**", "!**/dist", "!node_modules/**", "!src/generated/**"],
|
||||
"maxSize": 2097152
|
||||
},
|
||||
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
||||
"linter": {
|
||||
"rules": {
|
||||
"complexity": {
|
||||
"noUselessCatch": "off"
|
||||
},
|
||||
"correctness": {
|
||||
"noUnusedImports": "error",
|
||||
"noUnusedFunctionParameters": "error",
|
||||
"noUnusedVariables": "off"
|
||||
},
|
||||
"style": {
|
||||
"noNonNullAssertion": "off",
|
||||
"noParameterAssign": "error",
|
||||
"useAsConstAssertion": "error",
|
||||
"useDefaultParameterLast": "error",
|
||||
"useSelfClosingElements": "error",
|
||||
"useSingleVarDeclarator": "error",
|
||||
"noUnusedTemplateLiteral": "error",
|
||||
"useNumberNamespace": "error",
|
||||
"noInferrableTypes": "error",
|
||||
"noUselessElse": "error"
|
||||
},
|
||||
"suspicious": {
|
||||
"noExplicitAny": "off",
|
||||
"noRedeclare": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48976
openapi.json
Normal file
48976
openapi.json
Normal file
File diff suppressed because it is too large
Load Diff
83
package.json
83
package.json
@@ -1,88 +1,59 @@
|
||||
{
|
||||
"name": "@dokploy/cli",
|
||||
"description": "A CLI to manage dokploy server remotely",
|
||||
"version": "v0.2.8",
|
||||
"version": "0.3.0",
|
||||
"author": "Mauricio Siu",
|
||||
"licenses": [{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/Dokploy/cli/blob/master/LICENSE"
|
||||
}],
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/Dokploy/cli/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"bin": {
|
||||
"dokploy": "./bin/run.js"
|
||||
"dokploy": "./dist/index.js"
|
||||
},
|
||||
"bugs": "https://github.com/Dokploy/cli/issues",
|
||||
"dependencies": {
|
||||
"@oclif/core": "^3",
|
||||
"@oclif/plugin-help": "^6",
|
||||
"@oclif/plugin-plugins": "^5",
|
||||
"axios": "^1.7.2",
|
||||
"chalk": "^5.3.0",
|
||||
"cli-table3": "^0.6.5",
|
||||
"inquirer": "^9.2.23",
|
||||
"slugify": "^1.6.6",
|
||||
"superjson": "^2.2.1"
|
||||
"commander": "^13.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@oclif/prettier-config": "^0.2.1",
|
||||
"@oclif/test": "^4",
|
||||
"@types/chai": "^4",
|
||||
"@types/inquirer": "9.0.7",
|
||||
"@types/mocha": "^10",
|
||||
"@biomejs/biome": "2.1.1",
|
||||
"@types/node": "^18",
|
||||
"chai": "^4",
|
||||
"eslint": "^8",
|
||||
"eslint-config-oclif": "^5",
|
||||
"eslint-config-oclif-typescript": "^3",
|
||||
"eslint-config-prettier": "^9",
|
||||
"mocha": "^10",
|
||||
"oclif": "^4",
|
||||
"shx": "^0.3.3",
|
||||
"ts-node": "^10",
|
||||
"typescript": "^5"
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5",
|
||||
"vitest": "^4.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"files": [
|
||||
"/bin",
|
||||
"/dist",
|
||||
"/oclif.manifest.json"
|
||||
"/dist"
|
||||
],
|
||||
"homepage": "https://github.com/Dokploy/cli",
|
||||
"keywords": [
|
||||
"oclif"
|
||||
"dokploy",
|
||||
"cli"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"oclif": {
|
||||
"bin": "dokploy",
|
||||
"dirname": "dokploy",
|
||||
"commands": "./dist/commands",
|
||||
"plugins": [
|
||||
"@oclif/plugin-help",
|
||||
"@oclif/plugin-plugins"
|
||||
],
|
||||
"topicSeparator": " ",
|
||||
"topics": {
|
||||
"hello": {
|
||||
"description": "Say hello to the world and others"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": "Dokploy/cli",
|
||||
"scripts": {
|
||||
"build": "shx rm -rf dist && tsc -b",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"postpack": "shx rm -f oclif.manifest.json",
|
||||
"posttest": "pnpm run lint",
|
||||
"prepack": "oclif manifest && oclif readme",
|
||||
"test": "mocha --forbid-only \"test/**/*.test.ts\"",
|
||||
"version": "oclif readme && git add README.md",
|
||||
"publish" :"npm publish"
|
||||
"build": "tsc -b",
|
||||
"generate": "tsx scripts/generate.ts",
|
||||
"prebuild": "pnpm run generate",
|
||||
"dev": "tsx src/index.ts",
|
||||
"lint": "biome check --write .",
|
||||
"test": "vitest run",
|
||||
"publish": "npm publish"
|
||||
},
|
||||
"types": "dist/index.d.ts"
|
||||
"types": "dist/index.d.ts",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": ["esbuild"]
|
||||
}
|
||||
}
|
||||
|
||||
6330
pnpm-lock.yaml
generated
6330
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
217
readme.md
217
readme.md
@@ -1,131 +1,144 @@
|
||||
# Dokploy CLI
|
||||
|
||||
<!--  -->
|
||||
|
||||
Dokploy CLI is a powerful and versatile command-line tool designed to remotely manage your Dokploy server. It simplifies the process of creating, deploying, and managing applications and databases.
|
||||
|
||||
<!-- [](https://oclif.io)
|
||||
[](https://npmjs.org/package/dokploy)
|
||||
[](https://npmjs.org/package/dokploy)
|
||||
[](https://github.com/yourusername/dokploy/blob/master/package.json) -->
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Commands](#commands)
|
||||
- [Authentication](#authentication)
|
||||
- [Project Management](#project-management)
|
||||
- [Application Management](#application-management)
|
||||
- [Environment Management](#environment-management)
|
||||
- [Database Management](#database-management)
|
||||
- [Contributing](#contributing)
|
||||
- [Support](#support)
|
||||
- [License](#license)
|
||||
Dokploy CLI is a command-line tool to manage your Dokploy server remotely. It provides **449 commands** auto-generated from the Dokploy OpenAPI spec, covering every API endpoint.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh-session
|
||||
$ npm install -g @dokploy/cli
|
||||
```bash
|
||||
npm install -g @dokploy/cli
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### Option 1: Using the `auth` command
|
||||
|
||||
```bash
|
||||
dokploy auth -u https://panel.dokploy.com -t YOUR_API_KEY
|
||||
```
|
||||
|
||||
### Option 2: Environment variables
|
||||
|
||||
```bash
|
||||
export DOKPLOY_URL="https://panel.dokploy.com"
|
||||
export DOKPLOY_API_KEY="YOUR_API_KEY"
|
||||
```
|
||||
|
||||
### Option 3: `.env` file
|
||||
|
||||
Create a `.env` file in your working directory:
|
||||
|
||||
```env
|
||||
DOKPLOY_URL="https://panel.dokploy.com"
|
||||
DOKPLOY_API_KEY="YOUR_API_KEY"
|
||||
```
|
||||
|
||||
The CLI loads it automatically. Shell environment variables take priority over the `.env` file.
|
||||
|
||||
## Usage
|
||||
|
||||
```sh-session
|
||||
$ dokploy COMMAND
|
||||
running command...
|
||||
|
||||
$ dokploy --version
|
||||
dokploy/0.0.0 darwin-arm64 node-v18.18.0
|
||||
|
||||
$ dokploy --help [COMMAND]
|
||||
USAGE
|
||||
$ dokploy COMMAND
|
||||
...
|
||||
```bash
|
||||
dokploy <group> <action> [options]
|
||||
```
|
||||
|
||||
## Commands
|
||||
### Examples
|
||||
|
||||
### Authentication
|
||||
```bash
|
||||
# List all projects
|
||||
dokploy project all
|
||||
|
||||
- `dokploy authenticate`: Authenticate with the Dokploy server.
|
||||
- `dokploy verify`: Verify current authentication.
|
||||
# Get a specific project
|
||||
dokploy project one --projectId abc123
|
||||
|
||||
### Project Management
|
||||
# Create an application
|
||||
dokploy application create --name "my-app" --environmentId env123
|
||||
|
||||
- `dokploy project:create`: Create a new project.
|
||||
- `dokploy project:info`: Get information about an existing project.
|
||||
- `dokploy project:list`: List all projects.
|
||||
# Deploy an application
|
||||
dokploy application deploy --applicationId app123
|
||||
|
||||
### Environment Management
|
||||
# Create a postgres database
|
||||
dokploy postgres create --name "my-db" --environmentId env123
|
||||
|
||||
- `dokploy environment:create`: Create a new environment.
|
||||
- `dokploy environment:delete`: Delete an existing environment.
|
||||
# Stop a database
|
||||
dokploy postgres stop --postgresId pg123
|
||||
|
||||
### Application Management
|
||||
|
||||
- `dokploy app:create`: Create a new application.
|
||||
- `dokploy app:delete`: Delete an existing application.
|
||||
- `dokploy app:deploy`: Deploy an application.
|
||||
- `dokploy app:stop`: Stop a running application.
|
||||
|
||||
### Enviroment Management
|
||||
|
||||
- `dokploy env pull <file>`: Pull environment variables from Dokploy in a <file>.
|
||||
- `dokploy env push <file>`: Push environment variables to Dokploy from a <file>.
|
||||
|
||||
### Database Management
|
||||
|
||||
Dokploy supports various types of databases:
|
||||
|
||||
#### MariaDB
|
||||
|
||||
- `dokploy database:mariadb:create`
|
||||
- `dokploy database:mariadb:delete`
|
||||
- `dokploy database:mariadb:deploy`
|
||||
- `dokploy database:mariadb:stop`
|
||||
|
||||
#### MongoDB
|
||||
|
||||
- `dokploy database:mongo:create`
|
||||
- `dokploy database:mongo:delete`
|
||||
- `dokploy database:mongo:deploy`
|
||||
- `dokploy database:mongo:stop`
|
||||
|
||||
#### MySQL
|
||||
|
||||
- `dokploy database:mysql:create`
|
||||
- `dokploy database:mysql:delete`
|
||||
- `dokploy database:mysql:deploy`
|
||||
- `dokploy database:mysql:stop`
|
||||
|
||||
#### PostgreSQL
|
||||
|
||||
- `dokploy database:postgres:create`
|
||||
- `dokploy database:postgres:delete`
|
||||
- `dokploy database:postgres:deploy`
|
||||
- `dokploy database:postgres:stop`
|
||||
|
||||
#### Redis
|
||||
|
||||
- `dokploy database:redis:create`
|
||||
- `dokploy database:redis:delete`
|
||||
- `dokploy database:redis:deploy`
|
||||
- `dokploy database:redis:stop`
|
||||
|
||||
For more information about a specific command, use:
|
||||
|
||||
```sh-session
|
||||
$ dokploy [COMMAND] --help
|
||||
# Get raw JSON output
|
||||
dokploy project all --json
|
||||
```
|
||||
|
||||
### Getting help
|
||||
|
||||
```bash
|
||||
# List all groups
|
||||
dokploy --help
|
||||
|
||||
# List actions in a group
|
||||
dokploy application --help
|
||||
|
||||
# See options for a specific action
|
||||
dokploy application deploy --help
|
||||
```
|
||||
|
||||
## Available command groups
|
||||
|
||||
| Group | Commands | Group | Commands |
|
||||
|---|---|---|---|
|
||||
| `admin` | 1 | `notification` | 38 |
|
||||
| `ai` | 9 | `organization` | 10 |
|
||||
| `application` | 29 | `patch` | 12 |
|
||||
| `backup` | 11 | `port` | 4 |
|
||||
| `bitbucket` | 7 | `postgres` | 14 |
|
||||
| `certificates` | 4 | `preview-deployment` | 4 |
|
||||
| `cluster` | 4 | `project` | 8 |
|
||||
| `compose` | 28 | `redirects` | 4 |
|
||||
| `deployment` | 8 | `redis` | 14 |
|
||||
| `destination` | 6 | `registry` | 7 |
|
||||
| `docker` | 7 | `rollback` | 2 |
|
||||
| `domain` | 9 | `schedule` | 6 |
|
||||
| `environment` | 7 | `security` | 4 |
|
||||
| `gitea` | 8 | `server` | 16 |
|
||||
| `github` | 6 | `settings` | 49 |
|
||||
| `gitlab` | 7 | `ssh-key` | 6 |
|
||||
| `git-provider` | 2 | `sso` | 10 |
|
||||
| `license-key` | 6 | `stripe` | 7 |
|
||||
| `mariadb` | 14 | `swarm` | 3 |
|
||||
| `mongo` | 14 | `user` | 18 |
|
||||
| `mounts` | 6 | `volume-backups` | 6 |
|
||||
| `mysql` | 14 | | |
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pnpm install
|
||||
|
||||
# Run in dev mode
|
||||
pnpm run dev -- project all
|
||||
|
||||
# Regenerate commands from OpenAPI spec
|
||||
pnpm run generate
|
||||
|
||||
# Build
|
||||
pnpm run build
|
||||
|
||||
# Lint & format
|
||||
pnpm run lint
|
||||
```
|
||||
|
||||
### Updating commands
|
||||
|
||||
Commands are auto-generated from `openapi.json`. To update:
|
||||
|
||||
1. Replace `openapi.json` with the latest spec from the [Dokploy repo](https://github.com/Dokploy/dokploy)
|
||||
2. Run `pnpm run generate`
|
||||
3. Build with `pnpm run build`
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to contribute to Dokploy CLI, please check out our [Contributing Guide](https://github.com/Dokploy/cli/blob/main/CONTRIBUTING.md).
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter any issues or have any questions, please [open an issue](https://github.com/yourusername/dokploy/issues) in our GitHub repository.
|
||||
If you encounter any issues or have any questions, please [open an issue](https://github.com/Dokploy/cli/issues) in our GitHub repository.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
286
scripts/generate.ts
Normal file
286
scripts/generate.ts
Normal file
@@ -0,0 +1,286 @@
|
||||
/**
|
||||
* Generates CLI commands from the Dokploy OpenAPI spec.
|
||||
*
|
||||
* Usage: npx tsx scripts/generate.ts
|
||||
*
|
||||
* Reads openapi.json from the project root and generates:
|
||||
* - src/generated/commands.ts (all CLI commands)
|
||||
*/
|
||||
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(__dirname, "..");
|
||||
const SPEC_PATH = path.join(ROOT, "openapi.json");
|
||||
const OUT_PATH = path.join(ROOT, "src", "generated", "commands.ts");
|
||||
|
||||
interface OpenAPISpec {
|
||||
paths: Record<string, Record<string, OperationObject>>;
|
||||
}
|
||||
|
||||
interface OperationObject {
|
||||
operationId?: string;
|
||||
summary?: string;
|
||||
description?: string;
|
||||
tags?: string[];
|
||||
parameters?: ParameterObject[];
|
||||
requestBody?: {
|
||||
content?: {
|
||||
"application/json"?: {
|
||||
schema?: SchemaObject;
|
||||
};
|
||||
};
|
||||
};
|
||||
responses?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface ParameterObject {
|
||||
name: string;
|
||||
in: string;
|
||||
required?: boolean;
|
||||
schema?: SchemaObject;
|
||||
}
|
||||
|
||||
interface SchemaObject {
|
||||
type?: string;
|
||||
properties?: Record<string, SchemaProperty>;
|
||||
required?: string[];
|
||||
anyOf?: SchemaObject[];
|
||||
items?: SchemaObject;
|
||||
enum?: string[];
|
||||
}
|
||||
|
||||
interface SchemaProperty {
|
||||
type?: string;
|
||||
anyOf?: SchemaObject[];
|
||||
enum?: string[];
|
||||
default?: unknown;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface CommandInfo {
|
||||
/** e.g. "application.create" */
|
||||
endpoint: string;
|
||||
/** e.g. "application" */
|
||||
group: string;
|
||||
/** e.g. "create" */
|
||||
action: string;
|
||||
method: "get" | "post";
|
||||
description: string;
|
||||
options: OptionInfo[];
|
||||
}
|
||||
|
||||
interface OptionInfo {
|
||||
name: string;
|
||||
flag: string;
|
||||
description: string;
|
||||
required: boolean;
|
||||
type: "string" | "number" | "boolean";
|
||||
enumValues?: string[];
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function resolveType(prop: SchemaProperty): "string" | "number" | "boolean" {
|
||||
const raw = prop.type ?? prop.anyOf?.find((s) => s.type && s.type !== "null")?.type;
|
||||
if (raw === "number" || raw === "integer") return "number";
|
||||
if (raw === "boolean") return "boolean";
|
||||
return "string";
|
||||
}
|
||||
|
||||
function resolveEnum(prop: SchemaProperty): string[] | undefined {
|
||||
if (prop.enum) return prop.enum;
|
||||
const inner = prop.anyOf?.find((s) => s.enum);
|
||||
return inner?.enum;
|
||||
}
|
||||
|
||||
function extractOptionsFromSchema(schema: SchemaObject | undefined): OptionInfo[] {
|
||||
if (!schema?.properties) return [];
|
||||
const required = new Set(schema.required ?? []);
|
||||
return Object.entries(schema.properties).map(([name, prop]) => {
|
||||
const type = resolveType(prop);
|
||||
const enumValues = resolveEnum(prop);
|
||||
let desc = prop.description ?? name;
|
||||
if (enumValues) desc += ` (${enumValues.join(", ")})`;
|
||||
return {
|
||||
name,
|
||||
flag: `--${name} <${type === "boolean" ? "" : "value"}>`.replace(/ <>/g, ""),
|
||||
description: desc,
|
||||
required: required.has(name),
|
||||
type,
|
||||
enumValues,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function extractOptionsFromParams(params: ParameterObject[]): OptionInfo[] {
|
||||
return params.map((p) => {
|
||||
const type = resolveType(p.schema ?? {});
|
||||
const enumValues = resolveEnum(p.schema ?? {});
|
||||
let desc = p.name;
|
||||
if (enumValues) desc += ` (${enumValues.join(", ")})`;
|
||||
return {
|
||||
name: p.name,
|
||||
flag: `--${p.name} <${type === "boolean" ? "" : "value"}>`.replace(/ <>/g, ""),
|
||||
description: desc,
|
||||
required: p.required ?? false,
|
||||
type,
|
||||
enumValues,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function camelToKebab(s: string): string {
|
||||
return s.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Parse spec → CommandInfo[]
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function parseSpec(spec: OpenAPISpec): CommandInfo[] {
|
||||
const commands: CommandInfo[] = [];
|
||||
|
||||
for (const [pathKey, methods] of Object.entries(spec.paths)) {
|
||||
for (const [method, op] of Object.entries(methods)) {
|
||||
const endpoint = pathKey.replace(/^\//, "");
|
||||
const [group, ...rest] = endpoint.split(".");
|
||||
const action = rest.join(".");
|
||||
|
||||
if (!group || !action) continue;
|
||||
|
||||
const bodySchema = op.requestBody?.content?.["application/json"]?.schema;
|
||||
const paramOptions = op.parameters ? extractOptionsFromParams(op.parameters) : [];
|
||||
const bodyOptions = extractOptionsFromSchema(bodySchema);
|
||||
const options = [...paramOptions, ...bodyOptions];
|
||||
|
||||
commands.push({
|
||||
endpoint,
|
||||
group,
|
||||
action,
|
||||
method: method as "get" | "post",
|
||||
description: op.summary ?? op.description ?? `${group} ${action}`,
|
||||
options,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return commands.sort((a, b) => a.endpoint.localeCompare(b.endpoint));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Code generation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function generateOptionLine(opt: OptionInfo): string {
|
||||
const flag = opt.type === "boolean"
|
||||
? `--${opt.name}`
|
||||
: `--${opt.name} <value>`;
|
||||
const escaped = opt.description.replace(/'/g, "\\'");
|
||||
return opt.required
|
||||
? `.requiredOption('${flag}', '${escaped}')`
|
||||
: `.option('${flag}', '${escaped}')`;
|
||||
}
|
||||
|
||||
function generateCoercion(opt: OptionInfo): string {
|
||||
if (opt.type === "number") {
|
||||
return `if (opts["${opt.name}"] != null) opts["${opt.name}"] = Number(opts["${opt.name}"]);`;
|
||||
}
|
||||
if (opt.type === "boolean") {
|
||||
return `if (opts["${opt.name}"] != null) opts["${opt.name}"] = opts["${opt.name}"] === true || opts["${opt.name}"] === "true";`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function generateCommandCode(cmd: CommandInfo, groupVar: string): string {
|
||||
const actionName = camelToKebab(cmd.action);
|
||||
const optionLines = cmd.options.map(generateOptionLine).join("\n\t\t");
|
||||
const coercions = cmd.options
|
||||
.map(generateCoercion)
|
||||
.filter(Boolean)
|
||||
.map((c) => `\t\t\t${c}`)
|
||||
.join("\n");
|
||||
|
||||
const apiCall = cmd.method === "post"
|
||||
? `await apiPost("${cmd.endpoint}", opts)`
|
||||
: `await apiGet("${cmd.endpoint}", opts)`;
|
||||
|
||||
const escapedDesc = cmd.description.replace(/'/g, "\\'");
|
||||
|
||||
return `
|
||||
${groupVar}
|
||||
.command('${actionName}')
|
||||
.description('${escapedDesc}')
|
||||
${optionLines}
|
||||
.option('--json', 'Output raw JSON')
|
||||
.action(async (opts: Record<string, any>) => {
|
||||
const jsonOutput = opts.json; delete opts.json;
|
||||
${coercions}
|
||||
const data = ${apiCall};
|
||||
if (jsonOutput) {
|
||||
console.log(JSON.stringify(data, null, 2));
|
||||
} else {
|
||||
printOutput(data);
|
||||
}
|
||||
});`;
|
||||
}
|
||||
|
||||
function generateFile(commands: CommandInfo[]): string {
|
||||
// Group commands by their group name
|
||||
const groups = new Map<string, CommandInfo[]>();
|
||||
for (const cmd of commands) {
|
||||
const existing = groups.get(cmd.group) ?? [];
|
||||
existing.push(cmd);
|
||||
groups.set(cmd.group, existing);
|
||||
}
|
||||
|
||||
const groupBlocks: string[] = [];
|
||||
for (const [group, cmds] of [...groups.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
||||
const varName = `g_${group.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
||||
const kebabGroup = camelToKebab(group);
|
||||
groupBlocks.push(`\tconst ${varName} = program.command('${kebabGroup}').description('${kebabGroup} commands');`);
|
||||
for (const cmd of cmds) {
|
||||
groupBlocks.push(generateCommandCode(cmd, varName));
|
||||
}
|
||||
}
|
||||
|
||||
return `// Auto-generated from openapi.json — do not edit manually.
|
||||
// Run: npx tsx scripts/generate.ts
|
||||
|
||||
import type { Command } from "commander";
|
||||
import chalk from "chalk";
|
||||
import { apiPost, apiGet } from "../client.js";
|
||||
|
||||
function printOutput(data: unknown) {
|
||||
if (data === null || data === undefined) {
|
||||
console.log(chalk.green("OK"));
|
||||
return;
|
||||
}
|
||||
if (typeof data === "string") {
|
||||
console.log(data);
|
||||
return;
|
||||
}
|
||||
console.log(JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
export function registerGeneratedCommands(program: Command) {
|
||||
${groupBlocks.join("\n")}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const spec: OpenAPISpec = JSON.parse(fs.readFileSync(SPEC_PATH, "utf8"));
|
||||
const commands = parseSpec(spec);
|
||||
|
||||
fs.mkdirSync(path.dirname(OUT_PATH), { recursive: true });
|
||||
fs.writeFileSync(OUT_PATH, generateFile(commands));
|
||||
|
||||
console.log(`Generated ${commands.length} commands → ${path.relative(ROOT, OUT_PATH)}`);
|
||||
106
src/client.ts
Normal file
106
src/client.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import axios, { type AxiosInstance } from "axios";
|
||||
import chalk from "chalk";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const configPath = path.join(__dirname, "..", "config.json");
|
||||
|
||||
export interface AuthConfig {
|
||||
token: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
function loadEnvFile(): void {
|
||||
const envPath = path.resolve(process.cwd(), ".env");
|
||||
if (!fs.existsSync(envPath)) return;
|
||||
|
||||
const content = fs.readFileSync(envPath, "utf8");
|
||||
for (const line of content.split("\n")) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith("#")) continue;
|
||||
const eqIndex = trimmed.indexOf("=");
|
||||
if (eqIndex === -1) continue;
|
||||
const key = trimmed.slice(0, eqIndex).trim();
|
||||
const value = trimmed.slice(eqIndex + 1).trim().replace(/^["']|["']$/g, "");
|
||||
if (!process.env[key]) {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function readAuthConfig(): AuthConfig {
|
||||
loadEnvFile();
|
||||
|
||||
const envToken =
|
||||
process.env.DOKPLOY_API_KEY ?? process.env.DOKPLOY_AUTH_TOKEN;
|
||||
const envUrl = process.env.DOKPLOY_URL;
|
||||
|
||||
if (envToken && envUrl) {
|
||||
return { token: envToken, url: envUrl };
|
||||
}
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
"No configuration found. Please run 'dokploy auth' first or set DOKPLOY_URL and DOKPLOY_AUTH_TOKEN environment variables.",
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
||||
const { token, url } = config;
|
||||
|
||||
if (!url || !token) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
"Incomplete auth config. Run 'dokploy auth' or set environment variables.",
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return { token, url };
|
||||
}
|
||||
|
||||
export function saveAuthConfig(url: string, token: string): void {
|
||||
fs.writeFileSync(configPath, JSON.stringify({ url, token }, null, 2));
|
||||
}
|
||||
|
||||
export function createClient(): AxiosInstance {
|
||||
const auth = readAuthConfig();
|
||||
return axios.create({
|
||||
baseURL: `${auth.url}/api`,
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function apiPost(
|
||||
endpoint: string,
|
||||
data?: Record<string, unknown>,
|
||||
) {
|
||||
const client = createClient();
|
||||
const response = await client.post(
|
||||
`/trpc/${endpoint}`,
|
||||
data ? { json: data } : undefined,
|
||||
);
|
||||
return response.data?.result?.data?.json ?? response.data;
|
||||
}
|
||||
|
||||
export async function apiGet(
|
||||
endpoint: string,
|
||||
params?: Record<string, unknown>,
|
||||
) {
|
||||
const client = createClient();
|
||||
const query = params
|
||||
? `?input=${encodeURIComponent(JSON.stringify(params))}`
|
||||
: "";
|
||||
const response = await client.get(`/trpc/${endpoint}${query}`);
|
||||
return response.data?.result?.data?.json ?? response.data;
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
|
||||
import { type Project, getProjects } from "../../utils/shared.js";
|
||||
import { slugify } from "../../utils/slug.js";
|
||||
import { readAuthConfig } from "../../utils/utils.js";
|
||||
|
||||
export interface Answers {
|
||||
project: Project;
|
||||
}
|
||||
|
||||
export default class AppCreate extends Command {
|
||||
static description = "Create a new application within a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> app create"];
|
||||
|
||||
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,
|
||||
}),
|
||||
name: Flags.string({
|
||||
char: "n",
|
||||
description: "Application name",
|
||||
required: false,
|
||||
}),
|
||||
description: Flags.string({
|
||||
char: "d",
|
||||
description: "Application description",
|
||||
required: false,
|
||||
}),
|
||||
appName: Flags.string({
|
||||
description: "Docker app name",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(AppCreate);
|
||||
let { projectId, environmentId, name, description, appName } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to create the application in:",
|
||||
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:",
|
||||
name: "environment",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
environmentId = environment.environmentId;
|
||||
}
|
||||
|
||||
if (!name || !appName) {
|
||||
const appDetails = await inquirer.prompt([
|
||||
{
|
||||
message: "Enter the application name:",
|
||||
name: "name",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Application name is required"),
|
||||
default: name,
|
||||
},
|
||||
{
|
||||
message: "Enter the application description (optional):",
|
||||
name: "appDescription",
|
||||
type: "input",
|
||||
default: description,
|
||||
},
|
||||
]);
|
||||
|
||||
name = appDetails.name;
|
||||
description = appDetails.appDescription;
|
||||
|
||||
const appNamePrompt = await inquirer.prompt([
|
||||
{
|
||||
default: appName || `${slugify(name)}`,
|
||||
message: "Enter the App name:",
|
||||
name: "appName",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "App name is required"),
|
||||
},
|
||||
]);
|
||||
|
||||
appName = appNamePrompt.appName;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 application?',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirm.proceed) {
|
||||
this.error(chalk.yellow("Application creation cancelled."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/application.create`,
|
||||
{
|
||||
json: {
|
||||
name,
|
||||
appDescription: description,
|
||||
appName,
|
||||
projectId,
|
||||
environmentId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error creating application"));
|
||||
}
|
||||
|
||||
this.log(chalk.green(`Application '${name}' created successfully.`));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error creating application: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
|
||||
import { getProject, getProjects, type Application } from "../../utils/shared.js";
|
||||
import { readAuthConfig } from "../../utils/utils.js";
|
||||
import type { Answers } from "./create.js";
|
||||
|
||||
export default class AppDelete extends Command {
|
||||
static description = "Delete an application from a project.";
|
||||
|
||||
static examples = [
|
||||
"$ <%= config.bin %> app delete",
|
||||
"$ <%= config.bin %> app delete -p <projectId>",
|
||||
];
|
||||
|
||||
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,
|
||||
}),
|
||||
applicationId: Flags.string({
|
||||
char: 'a',
|
||||
description: 'ID of the application to delete',
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: 'y',
|
||||
description: 'Skip confirmation prompt',
|
||||
default: false,
|
||||
})
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(AppDelete);
|
||||
let { projectId, environmentId, applicationId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to delete the application 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:",
|
||||
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: selectedEnvironment.applications.map((app: Application) => ({
|
||||
name: app.name,
|
||||
value: app.applicationId,
|
||||
})),
|
||||
message: "Select the application to delete:",
|
||||
name: "selectedApp",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
applicationId = appAnswers.selectedApp;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 application?",
|
||||
name: "confirmDelete",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDelete) {
|
||||
this.error(chalk.yellow("Application deletion cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const deleteResponse = await axios.post(
|
||||
`${auth.url}/api/trpc/application.delete`,
|
||||
{
|
||||
json: {
|
||||
applicationId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!deleteResponse.data.result.data.json) {
|
||||
this.error(chalk.red("Error deleting application"));
|
||||
}
|
||||
|
||||
this.log(chalk.green("Application deleted successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Failed to delete application: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import { readAuthConfig } from "../../utils/utils.js";
|
||||
import chalk from "chalk";
|
||||
import { getProject, getProjects, type Application } from "../../utils/shared.js";
|
||||
import inquirer from "inquirer";
|
||||
import type { Answers } from "./create.js";
|
||||
import axios from "axios";
|
||||
|
||||
export default class AppDeploy extends Command {
|
||||
static description = "Deploy an application to a project.";
|
||||
|
||||
static examples = [
|
||||
"$ <%= config.bin %> app deploy",
|
||||
"$ <%= config.bin %> app deploy --applicationId myAppId",
|
||||
"$ DOKPLOY_URL=xxx DOKPLOY_AUTH_TOKEN=xxx <%= config.bin %> app deploy --applicationId myAppId"
|
||||
];
|
||||
|
||||
static flags = {
|
||||
applicationId: Flags.string({
|
||||
char: 'a',
|
||||
description: 'ID of the application to deploy',
|
||||
required: false,
|
||||
}),
|
||||
projectId: Flags.string({
|
||||
char: 'p',
|
||||
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',
|
||||
default: false,
|
||||
})
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(AppDeploy);
|
||||
let { projectId, applicationId, environmentId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to deploy the application 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:",
|
||||
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: selectedEnvironment.applications.map((app: Application) => ({
|
||||
name: app.name,
|
||||
value: app.applicationId,
|
||||
})),
|
||||
message: "Select the application to deploy:",
|
||||
name: "selectedApp",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
applicationId = appAnswers.selectedApp;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirmar si no se especifica --skipConfirm
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to deploy this application?",
|
||||
name: "confirmDelete",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDelete) {
|
||||
this.error(chalk.yellow("Application deployment cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/application.deploy`,
|
||||
{
|
||||
json: {
|
||||
applicationId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error deploying application"));
|
||||
}
|
||||
this.log(chalk.green("Application deploy successful."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error deploying application: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import { readAuthConfig } from "../../utils/utils.js";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
import { getProject, getProjects, type Application } from "../../utils/shared.js";
|
||||
import type { Answers } from "./create.js";
|
||||
import axios from "axios";
|
||||
|
||||
export default class AppStop extends Command {
|
||||
static description = "Stop an application from a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> app 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,
|
||||
}),
|
||||
applicationId: Flags.string({
|
||||
char: 'a',
|
||||
description: 'ID of the application to stop',
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: 'y',
|
||||
description: 'Skip confirmation prompt',
|
||||
default: false,
|
||||
})
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(AppStop);
|
||||
let { projectId, environmentId, applicationId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to stop the application 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:",
|
||||
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: selectedEnvironment.applications.map((app: Application) => ({
|
||||
name: app.name,
|
||||
value: app.applicationId,
|
||||
})),
|
||||
message: "Select the application to stop:",
|
||||
name: "selectedApp",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
applicationId = appAnswers.selectedApp;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirmar si no se especifica --skipConfirm
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to stop this application?",
|
||||
name: "confirmDelete",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDelete) {
|
||||
this.error(chalk.yellow("Application stop cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/application.stop`,
|
||||
{
|
||||
json: {
|
||||
applicationId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error stopping application"));
|
||||
}
|
||||
this.log(chalk.green("Application stop successful."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error stopping application: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/commands/auth.ts
Normal file
38
src/commands/auth.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import type { Command } from "commander";
|
||||
import { saveAuthConfig } from "../client.js";
|
||||
|
||||
export function registerAuthCommand(program: Command) {
|
||||
program
|
||||
.command("auth")
|
||||
.description("Authenticate with your Dokploy server")
|
||||
.requiredOption(
|
||||
"-u, --url <url>",
|
||||
"Server URL (e.g., https://panel.dokploy.com)",
|
||||
)
|
||||
.requiredOption(
|
||||
"-t, --token <token>",
|
||||
"API key from your Dokploy dashboard",
|
||||
)
|
||||
.action(async (opts: { url: string; token: string }) => {
|
||||
const url = opts.url.replace(/\/+$/, "");
|
||||
|
||||
console.log(chalk.blue("Validating credentials..."));
|
||||
|
||||
try {
|
||||
await axios.get(`${url}/api/trpc/user.get`, {
|
||||
headers: {
|
||||
"x-api-key": opts.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
saveAuthConfig(url, opts.token);
|
||||
console.log(chalk.green("Authenticated successfully."));
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red(`Authentication failed: ${error.message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer, { type Answers, type QuestionCollection } from "inquirer";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const configPath = path.join(__dirname, "..", "..", "config.json");
|
||||
|
||||
export default class Authenticate extends Command {
|
||||
static description = "Authenticate the user by saving server URL and token";
|
||||
|
||||
static examples = [
|
||||
"$ <%= config.bin %> <%= command.id %> --url=https://panel.dokploy.com --token=MRTHGZDGMRZWM43EMZSHGZTTMRTHGZDGONSGMZDTMY",
|
||||
"$ <%= config.bin %> <%= command.id %> -u https://panel.dokploy.com -t MRTHGZDGMRZWM43EMZSHGZTTMRTHGZDGONSGMZDTMY",
|
||||
];
|
||||
|
||||
static flags = {
|
||||
token: Flags.string({
|
||||
char: "t",
|
||||
description: "Authentication token",
|
||||
}),
|
||||
url: Flags.string({
|
||||
char: "u",
|
||||
description: "Server URL",
|
||||
}),
|
||||
};
|
||||
|
||||
async run() {
|
||||
console.log(
|
||||
chalk.blue.bold("\n Welcome to Dokploy CLI Authentication \n"),
|
||||
);
|
||||
|
||||
const { flags } = await this.parse(Authenticate);
|
||||
|
||||
let answers: Answers = {};
|
||||
|
||||
const questions: QuestionCollection[] = [];
|
||||
|
||||
let config: { token?: string; url?: string } = {};
|
||||
if (fs.existsSync(configPath)) {
|
||||
const configFileContent = fs.readFileSync(configPath, "utf8");
|
||||
config = JSON.parse(configFileContent);
|
||||
}
|
||||
|
||||
if (!flags.url) {
|
||||
questions.push({
|
||||
default: config.url,
|
||||
message: chalk.green(
|
||||
"Enter your server URL (e.g., https://panel.dokploy.com): ",
|
||||
),
|
||||
name: "url",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Server URL is required"),
|
||||
});
|
||||
}
|
||||
|
||||
if (!flags.token) {
|
||||
questions.push({
|
||||
default: config.token,
|
||||
message: chalk.green(
|
||||
"Enter your authentication token (e.g., MRTHGZDGMRZWM43EMZSHGZTTMRTHGZDGONSGMZDTMY=): ",
|
||||
),
|
||||
name: "token",
|
||||
type: "input",
|
||||
validate: (input) =>
|
||||
input ? true : "Authentication token is required",
|
||||
});
|
||||
}
|
||||
|
||||
if (questions.length > 0) {
|
||||
answers = await inquirer.prompt(questions);
|
||||
}
|
||||
|
||||
const url = flags.url || answers.url;
|
||||
const token = flags.token || answers.token;
|
||||
|
||||
config.token = token;
|
||||
config.url = url;
|
||||
|
||||
try {
|
||||
console.log(`\n${chalk.blue("Validating server...")}`);
|
||||
|
||||
await axios.get(
|
||||
`${url}/api/trpc/user.get`,
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
||||
this.log(chalk.green("Authentication details saved successfully."));
|
||||
} catch (error) {
|
||||
this.error(
|
||||
// @ts-expect-error - Type
|
||||
chalk.red(`Failed to save authentication details: ${error.message}`),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import { getProjects, type Database } from "../../../utils/shared.js";
|
||||
import { slugify } from "../../../utils/slug.js";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
|
||||
export default class DatabaseMariadbCreate extends Command {
|
||||
static description = "Create a new MariaDB database within a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> mariadb create"];
|
||||
|
||||
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,
|
||||
}),
|
||||
name: Flags.string({
|
||||
char: "n",
|
||||
description: "Database name",
|
||||
required: false,
|
||||
}),
|
||||
databaseName: Flags.string({
|
||||
description: "MariaDB database name",
|
||||
required: false,
|
||||
}),
|
||||
description: Flags.string({
|
||||
char: "d",
|
||||
description: "Database description",
|
||||
required: false,
|
||||
}),
|
||||
databaseRootPassword: Flags.string({
|
||||
description: "Database root password",
|
||||
required: false,
|
||||
}),
|
||||
databasePassword: Flags.string({
|
||||
description: "Database password",
|
||||
required: false,
|
||||
}),
|
||||
databaseUser: Flags.string({
|
||||
description: "Database user",
|
||||
default: "mariadb",
|
||||
}),
|
||||
dockerImage: Flags.string({
|
||||
description: "Docker image",
|
||||
default: "mariadb:11",
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
appName: Flags.string({
|
||||
description: "App name",
|
||||
required: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMariadbCreate);
|
||||
let {
|
||||
projectId,
|
||||
environmentId,
|
||||
name,
|
||||
databaseName,
|
||||
description,
|
||||
databaseRootPassword,
|
||||
databasePassword,
|
||||
databaseUser,
|
||||
dockerImage,
|
||||
appName
|
||||
} = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to create the MariaDB instance in:",
|
||||
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:",
|
||||
name: "environment",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
environmentId = environment.environmentId;
|
||||
}
|
||||
|
||||
if (!name || !databaseName || !appName) {
|
||||
const dbDetails = await inquirer.prompt([
|
||||
{
|
||||
message: "Enter the name:",
|
||||
name: "name",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Database name is required"),
|
||||
default: name,
|
||||
},
|
||||
{
|
||||
message: "Database name:",
|
||||
name: "databaseName",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Database name is required"),
|
||||
default: databaseName,
|
||||
},
|
||||
{
|
||||
message: "Enter the database description (optional):",
|
||||
name: "description",
|
||||
type: "input",
|
||||
default: description,
|
||||
},
|
||||
{
|
||||
message: "Database Root Password (optional):",
|
||||
name: "databaseRootPassword",
|
||||
type: "password",
|
||||
default: databaseRootPassword,
|
||||
},
|
||||
{
|
||||
message: "Database password (optional):",
|
||||
name: "databasePassword",
|
||||
type: "password",
|
||||
default: databasePassword,
|
||||
},
|
||||
{
|
||||
default: dockerImage || "mariadb:11",
|
||||
message: "Docker Image (default: mariadb:11):",
|
||||
name: "dockerImage",
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
default: databaseUser || "mariadb",
|
||||
message: "Database User: (default: mariadb):",
|
||||
name: "databaseUser",
|
||||
type: "input",
|
||||
},
|
||||
]);
|
||||
|
||||
name = dbDetails.name;
|
||||
databaseName = dbDetails.databaseName;
|
||||
description = dbDetails.description;
|
||||
databaseRootPassword = dbDetails.databaseRootPassword;
|
||||
databasePassword = dbDetails.databasePassword;
|
||||
dockerImage = dbDetails.dockerImage;
|
||||
databaseUser = dbDetails.databaseUser;
|
||||
|
||||
const appNamePrompt = await inquirer.prompt([
|
||||
{
|
||||
default: appName || `${slugify(name)}`,
|
||||
message: "Enter the App name:",
|
||||
name: "appName",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "App name is required"),
|
||||
},
|
||||
]);
|
||||
|
||||
appName = appNamePrompt.appName;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 MariaDB instance?',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirm.proceed) {
|
||||
this.error(chalk.yellow("MariaDB creation cancelled."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mariadb.create`,
|
||||
{
|
||||
json: {
|
||||
name,
|
||||
databaseName,
|
||||
description,
|
||||
databaseRootPassword,
|
||||
databasePassword,
|
||||
databaseUser,
|
||||
dockerImage,
|
||||
appName,
|
||||
projectId,
|
||||
environmentId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
this.error(chalk.red("Error creating MariaDB instance", response.data.result.data.json));
|
||||
}
|
||||
|
||||
this.log(chalk.green(`MariaDB instance '${name}' created successfully.`));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error creating MariaDB instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
|
||||
export default class DatabaseMariadbDelete extends Command {
|
||||
static description = "Delete a MariaDB database from a project.";
|
||||
static examples = [
|
||||
"$ <%= config.bin %> mariadb delete",
|
||||
"$ <%= config.bin %> mariadb delete -p <projectId>",
|
||||
];
|
||||
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,
|
||||
}),
|
||||
mariadbId: Flags.string({
|
||||
char: "m",
|
||||
description: "ID of the MariaDB instance to delete",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMariadbDelete);
|
||||
let { projectId, environmentId, mariadbId } = flags;
|
||||
|
||||
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([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project.projectId,
|
||||
})),
|
||||
message: "Select a project to delete the MariaDB instance from:",
|
||||
name: "selectedProject",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
selectedProject = projects.find(p => p.projectId === answers.selectedProject);
|
||||
projectId = answers.selectedProject;
|
||||
} 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",
|
||||
},
|
||||
]);
|
||||
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([
|
||||
{
|
||||
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) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to delete this MariaDB instance?",
|
||||
name: "confirmDelete",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDelete) {
|
||||
this.error(chalk.yellow("MariaDB deletion cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mariadb.remove`,
|
||||
{
|
||||
json: {
|
||||
mariadbId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
this.error(chalk.red("Error deleting MariaDB instance"));
|
||||
}
|
||||
this.log(chalk.green("MariaDB instance deleted successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error deleting MariaDB instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import chalk from "chalk";
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import inquirer from "inquirer";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
import axios from "axios";
|
||||
|
||||
export default class DatabaseMariadbDeploy extends Command {
|
||||
static description = "Deploy an mariadb to a project.";
|
||||
|
||||
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,
|
||||
}),
|
||||
mariadbId: Flags.string({
|
||||
char: "m",
|
||||
description: "ID of the MariaDB instance to deploy",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMariadbDeploy);
|
||||
let { projectId, environmentId, mariadbId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to deploy the MariaDB in:",
|
||||
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:",
|
||||
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([
|
||||
{
|
||||
choices: selectedEnvironment.mariadb.map((db: Database) => ({
|
||||
name: db.name,
|
||||
value: db.mariadbId,
|
||||
})),
|
||||
message: "Select the MariaDB instance to deploy:",
|
||||
name: "selectedDb",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
mariadbId = dbAnswers.selectedDb;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirmar si no se especifica --skipConfirm
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to deploy this MariaDB instance?",
|
||||
name: "confirmDeploy",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDeploy) {
|
||||
this.error(chalk.yellow("MariaDB deployment cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mariadb.deploy`,
|
||||
{
|
||||
json: {
|
||||
mariadbId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error deploying MariaDB instance"));
|
||||
}
|
||||
this.log(chalk.green("MariaDB instance deployed successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error deploying MariaDB instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
import axios from "axios";
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
|
||||
export default class DatabaseMariadbStop extends Command {
|
||||
static description = "Stop an mariadb from a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> mariadb 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,
|
||||
}),
|
||||
mariadbId: Flags.string({
|
||||
char: "m",
|
||||
description: "ID of the MariaDB instance to stop",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMariadbStop);
|
||||
let { projectId, environmentId, mariadbId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to stop the MariaDB instance 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:",
|
||||
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([
|
||||
{
|
||||
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
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to stop this MariaDB instance?",
|
||||
name: "confirmStop",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmStop) {
|
||||
this.error(chalk.yellow("MariaDB stop cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mariadb.stop`,
|
||||
{
|
||||
json: {
|
||||
mariadbId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error stopping MariaDB instance"));
|
||||
}
|
||||
this.log(chalk.green("MariaDB instance stopped successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error stopping MariaDB instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import { getProjects, type Database } from "../../../utils/shared.js";
|
||||
import { slugify } from "../../../utils/slug.js";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
|
||||
export default class DatabaseMongoCreate extends Command {
|
||||
static description = "Create a new MongoDB database within a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> mongo create"];
|
||||
|
||||
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,
|
||||
}),
|
||||
name: Flags.string({
|
||||
char: "n",
|
||||
description: "Database name",
|
||||
required: false,
|
||||
}),
|
||||
databaseName: Flags.string({
|
||||
description: "MongoDB database name",
|
||||
required: false,
|
||||
}),
|
||||
description: Flags.string({
|
||||
char: "d",
|
||||
description: "Database description",
|
||||
required: false,
|
||||
}),
|
||||
databasePassword: Flags.string({
|
||||
description: "Database password",
|
||||
required: false,
|
||||
}),
|
||||
databaseUser: Flags.string({
|
||||
description: "Database user",
|
||||
default: "mongo",
|
||||
}),
|
||||
dockerImage: Flags.string({
|
||||
description: "Docker image",
|
||||
default: "mongo:6",
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
appName: Flags.string({
|
||||
description: "App name",
|
||||
required: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMongoCreate);
|
||||
let {
|
||||
projectId,
|
||||
environmentId,
|
||||
name,
|
||||
databaseName,
|
||||
description,
|
||||
databasePassword,
|
||||
databaseUser,
|
||||
dockerImage,
|
||||
appName
|
||||
} = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to create the MongoDB instance in:",
|
||||
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:",
|
||||
name: "environment",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
environmentId = environment.environmentId;
|
||||
}
|
||||
|
||||
if (!name || !databaseName || !appName || !databasePassword) {
|
||||
const dbDetails = await inquirer.prompt([
|
||||
{
|
||||
message: "Enter the name:",
|
||||
name: "name",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Database name is required"),
|
||||
default: name,
|
||||
},
|
||||
{
|
||||
message: "Database name:",
|
||||
name: "databaseName",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Database name is required"),
|
||||
default: databaseName,
|
||||
},
|
||||
{
|
||||
message: "Enter the database description (optional):",
|
||||
name: "description",
|
||||
type: "input",
|
||||
default: description,
|
||||
},
|
||||
{
|
||||
message: "Database password (optional):",
|
||||
name: "databasePassword",
|
||||
type: "password",
|
||||
default: databasePassword,
|
||||
},
|
||||
{
|
||||
default: dockerImage || "mongo:6",
|
||||
message: "Docker Image (default: mongo:6):",
|
||||
name: "dockerImage",
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
default: databaseUser || "mongo",
|
||||
message: "Database User: (default: mongo):",
|
||||
name: "databaseUser",
|
||||
type: "input",
|
||||
},
|
||||
]);
|
||||
|
||||
name = dbDetails.name;
|
||||
databaseName = dbDetails.databaseName;
|
||||
description = dbDetails.description;
|
||||
databasePassword = dbDetails.databasePassword;
|
||||
dockerImage = dbDetails.dockerImage;
|
||||
databaseUser = dbDetails.databaseUser;
|
||||
|
||||
const appNamePrompt = await inquirer.prompt([
|
||||
{
|
||||
default: appName || `${slugify(name)}`,
|
||||
message: "Enter the App name:",
|
||||
name: "appName",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "App name is required"),
|
||||
},
|
||||
]);
|
||||
|
||||
appName = appNamePrompt.appName;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 MongoDB instance?',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirm.proceed) {
|
||||
this.error(chalk.yellow("MongoDB creation cancelled."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mongo.create`,
|
||||
{
|
||||
json: {
|
||||
name,
|
||||
databaseName,
|
||||
description,
|
||||
databasePassword,
|
||||
databaseUser,
|
||||
dockerImage,
|
||||
appName,
|
||||
projectId,
|
||||
environmentId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
this.error(chalk.red("Error creating MongoDB instance"));
|
||||
}
|
||||
|
||||
this.log(chalk.green(`MongoDB instance '${name}' created successfully.`));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error creating MongoDB instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
|
||||
import { readAuthConfig } from "../../../utils/utils.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.";
|
||||
|
||||
static examples = [
|
||||
"$ <%= config.bin %> mongo delete",
|
||||
"$ <%= config.bin %> mongo delete -p <projectId>",
|
||||
];
|
||||
|
||||
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,
|
||||
}),
|
||||
mongoId: Flags.string({
|
||||
char: "m",
|
||||
description: "ID of the MongoDB instance to delete",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMongoDelete);
|
||||
let { projectId, environmentId, mongoId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project.projectId,
|
||||
})),
|
||||
message: "Select a project to delete the MongoDB instance from:",
|
||||
name: "selectedProject",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
selectedProject = projects.find(p => p.projectId === answers.selectedProject);
|
||||
projectId = answers.selectedProject;
|
||||
} 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",
|
||||
},
|
||||
]);
|
||||
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([
|
||||
{
|
||||
choices: selectedEnvironment.mongo.map((db: Database) => ({
|
||||
name: db.name,
|
||||
value: db.mongoId,
|
||||
})),
|
||||
message: "Select the MongoDB instance to delete:",
|
||||
name: "selectedDb",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
mongoId = dbAnswers.selectedDb;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 MongoDB instance?",
|
||||
name: "confirmDelete",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDelete) {
|
||||
this.error(chalk.yellow("MongoDB deletion cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mongo.remove`,
|
||||
{
|
||||
json: {
|
||||
mongoId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
this.error(chalk.red("Error deleting MongoDB instance"));
|
||||
}
|
||||
this.log(chalk.green("MongoDB instance deleted successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error deleting MongoDB instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import chalk from "chalk";
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import inquirer from "inquirer";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
import axios from "axios";
|
||||
|
||||
export default class DatabaseMongoDeploy extends Command {
|
||||
static description = "Deploy an mongo to a project.";
|
||||
|
||||
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,
|
||||
}),
|
||||
mongoId: Flags.string({
|
||||
char: "m",
|
||||
description: "ID of the MongoDB instance to deploy",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMongoDeploy);
|
||||
let { projectId, environmentId, mongoId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to deploy the MongoDB instance 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:",
|
||||
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([
|
||||
{
|
||||
choices: selectedEnvironment.mongo.map((db: Database) => ({
|
||||
name: db.name,
|
||||
value: db.mongoId,
|
||||
})),
|
||||
message: "Select the MongoDB instance to deploy:",
|
||||
name: "selectedDb",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
mongoId = dbAnswers.selectedDb;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirmar si no se especifica --skipConfirm
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to deploy this MongoDB instance?",
|
||||
name: "confirmDeploy",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDeploy) {
|
||||
this.error(chalk.yellow("MongoDB deployment cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mongo.deploy`,
|
||||
{
|
||||
json: {
|
||||
mongoId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error deploying MongoDB instance"));
|
||||
}
|
||||
this.log(chalk.green("MongoDB instance deployed successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error deploying MongoDB instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
import axios from "axios";
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
|
||||
export default class DatabaseMongoStop extends Command {
|
||||
static description = "Stop an mongo from a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> mongo 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,
|
||||
}),
|
||||
mongoId: Flags.string({
|
||||
char: "m",
|
||||
description: "ID of the MongoDB instance to stop",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMongoStop);
|
||||
let { projectId, environmentId, mongoId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to stop the MongoDB instance 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:",
|
||||
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([
|
||||
{
|
||||
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
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to stop this MongoDB instance?",
|
||||
name: "confirmStop",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmStop) {
|
||||
this.error(chalk.yellow("MongoDB stop cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mongo.stop`,
|
||||
{
|
||||
json: {
|
||||
mongoId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error stopping MongoDB instance"));
|
||||
}
|
||||
this.log(chalk.green("MongoDB instance stopped successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error stopping MongoDB instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
|
||||
import { slugify } from "../../../utils/slug.js";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import { getProjects, type Database } from "../../../utils/shared.js";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
|
||||
export default class DatabaseMysqlCreate extends Command {
|
||||
static description = "Create a new MySQL database within a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> mysql create"];
|
||||
|
||||
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,
|
||||
}),
|
||||
name: Flags.string({
|
||||
char: "n",
|
||||
description: "Database name",
|
||||
required: false,
|
||||
}),
|
||||
databaseName: Flags.string({
|
||||
description: "MySQL database name",
|
||||
required: false,
|
||||
}),
|
||||
description: Flags.string({
|
||||
char: "d",
|
||||
description: "Database description",
|
||||
required: false,
|
||||
}),
|
||||
databaseRootPassword: Flags.string({
|
||||
description: "Database root password",
|
||||
required: false,
|
||||
}),
|
||||
databasePassword: Flags.string({
|
||||
description: "Database password",
|
||||
required: false,
|
||||
}),
|
||||
databaseUser: Flags.string({
|
||||
description: "Database user",
|
||||
default: "mysql",
|
||||
}),
|
||||
dockerImage: Flags.string({
|
||||
description: "Docker image",
|
||||
default: "mysql:8",
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
appName: Flags.string({
|
||||
description: "App name",
|
||||
required: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMysqlCreate);
|
||||
let {
|
||||
projectId,
|
||||
environmentId,
|
||||
name,
|
||||
databaseName,
|
||||
description,
|
||||
databaseRootPassword,
|
||||
databasePassword,
|
||||
databaseUser,
|
||||
dockerImage,
|
||||
appName
|
||||
} = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to create the MySQL instance in:",
|
||||
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:",
|
||||
name: "environment",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
environmentId = environment.environmentId;
|
||||
}
|
||||
|
||||
if (!name || !databaseName || !appName || !databasePassword || !databaseRootPassword) {
|
||||
const dbDetails = await inquirer.prompt([
|
||||
{
|
||||
message: "Enter the name:",
|
||||
name: "name",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Database name is required"),
|
||||
default: name,
|
||||
},
|
||||
{
|
||||
message: "Database name:",
|
||||
name: "databaseName",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Database name is required"),
|
||||
default: databaseName,
|
||||
},
|
||||
{
|
||||
message: "Enter the database description (optional):",
|
||||
name: "description",
|
||||
type: "input",
|
||||
default: description,
|
||||
},
|
||||
{
|
||||
message: "Database Root Password:",
|
||||
name: "databaseRootPassword",
|
||||
type: "password",
|
||||
default: databaseRootPassword,
|
||||
},
|
||||
{
|
||||
message: "Database password:",
|
||||
name: "databasePassword",
|
||||
type: "password",
|
||||
default: databasePassword,
|
||||
},
|
||||
{
|
||||
default: dockerImage || "mysql:8",
|
||||
message: "Docker Image (default: mysql:8):",
|
||||
name: "dockerImage",
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
default: databaseUser || "mysql",
|
||||
message: "Database User: (default: mysql):",
|
||||
name: "databaseUser",
|
||||
type: "input",
|
||||
},
|
||||
]);
|
||||
|
||||
name = dbDetails.name;
|
||||
databaseName = dbDetails.databaseName;
|
||||
description = dbDetails.description;
|
||||
databaseRootPassword = dbDetails.databaseRootPassword;
|
||||
databasePassword = dbDetails.databasePassword;
|
||||
dockerImage = dbDetails.dockerImage;
|
||||
databaseUser = dbDetails.databaseUser;
|
||||
|
||||
const appNamePrompt = await inquirer.prompt([
|
||||
{
|
||||
default: appName || `${slugify(name)}`,
|
||||
message: "Enter the App name:",
|
||||
name: "appName",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "App name is required"),
|
||||
},
|
||||
]);
|
||||
|
||||
appName = appNamePrompt.appName;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 MySQL instance?',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirm.proceed) {
|
||||
this.error(chalk.yellow("MySQL creation cancelled."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mysql.create`,
|
||||
{
|
||||
json: {
|
||||
name,
|
||||
databaseName,
|
||||
description,
|
||||
databaseRootPassword,
|
||||
databasePassword,
|
||||
databaseUser,
|
||||
dockerImage,
|
||||
appName,
|
||||
projectId,
|
||||
environmentId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
this.error(chalk.red("Error creating MySQL instance", response.data.result.data.json));
|
||||
}
|
||||
|
||||
this.log(chalk.green(`MySQL instance '${name}' created successfully.`));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error creating MySQL instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
|
||||
import { readAuthConfig } from "../../../utils/utils.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.";
|
||||
|
||||
static examples = [
|
||||
"$ <%= config.bin %> mysql delete",
|
||||
"$ <%= config.bin %> mysql delete -p <projectId>",
|
||||
];
|
||||
|
||||
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<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMysqlDelete);
|
||||
let { projectId, environmentId, mysqlId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project.projectId,
|
||||
})),
|
||||
message: "Select a project to delete the MySQL instance from:",
|
||||
name: "selectedProject",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
selectedProject = projects.find(p => p.projectId === answers.selectedProject);
|
||||
projectId = answers.selectedProject;
|
||||
} 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",
|
||||
},
|
||||
]);
|
||||
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([
|
||||
{
|
||||
choices: selectedEnvironment.mysql.map((db: Database) => ({
|
||||
name: db.name,
|
||||
value: db.mysqlId,
|
||||
})),
|
||||
message: "Select the MySQL instance to delete:",
|
||||
name: "selectedDb",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
mysqlId = dbAnswers.selectedDb;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 MySQL instance?",
|
||||
name: "confirmDelete",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDelete) {
|
||||
this.error(chalk.yellow("MySQL deletion cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mysql.remove`,
|
||||
{
|
||||
json: {
|
||||
mysqlId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
this.error(chalk.red("Error deleting MySQL instance"));
|
||||
}
|
||||
this.log(chalk.green("MySQL instance deleted successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error deleting MySQL instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import chalk from "chalk";
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import inquirer from "inquirer";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
import axios from "axios";
|
||||
|
||||
export default class DatabaseMysqlDeploy extends Command {
|
||||
static description = "Deploy an mysql to a project.";
|
||||
|
||||
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<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMysqlDeploy);
|
||||
let { projectId, environmentId, mysqlId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to deploy the MySQL instance 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:",
|
||||
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([
|
||||
{
|
||||
choices: selectedEnvironment.mysql.map((db: Database) => ({
|
||||
name: db.name,
|
||||
value: db.mysqlId,
|
||||
})),
|
||||
message: "Select the MySQL instance to deploy:",
|
||||
name: "selectedDb",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
mysqlId = dbAnswers.selectedDb;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirmar si no se especifica --skipConfirm
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to deploy this MySQL instance?",
|
||||
name: "confirmDeploy",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDeploy) {
|
||||
this.error(chalk.yellow("MySQL deployment cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mysql.deploy`,
|
||||
{
|
||||
json: {
|
||||
mysqlId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error deploying MySQL instance"));
|
||||
}
|
||||
this.log(chalk.green("MySQL instance deployed successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error deploying MySQL instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
import axios from "axios";
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
|
||||
export default class DatabaseMysqlStop extends Command {
|
||||
static description = "Stop an mysql from a project.";
|
||||
|
||||
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<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseMysqlStop);
|
||||
let { projectId, environmentId, mysqlId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to stop the MySQL instance 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:",
|
||||
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([
|
||||
{
|
||||
choices: selectedEnvironment.mysql.map((db: Database) => ({
|
||||
name: db.name,
|
||||
value: db.mysqlId,
|
||||
})),
|
||||
message: "Select the MySQL instance to stop:",
|
||||
name: "selectedDb",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
mysqlId = dbAnswers.selectedDb;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirmar si no se especifica --skipConfirm
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to stop this MySQL instance?",
|
||||
name: "confirmStop",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmStop) {
|
||||
this.error(chalk.yellow("MySQL stop cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/mysql.stop`,
|
||||
{
|
||||
json: {
|
||||
mysqlId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error stopping MySQL instance"));
|
||||
}
|
||||
this.log(chalk.green("MySQL instance stopped successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error stopping MySQL instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
import { slugify } from "../../../utils/slug.js";
|
||||
import { readAuthConfig } from "../../../utils/utils.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.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> postgres create"];
|
||||
|
||||
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,
|
||||
}),
|
||||
name: Flags.string({
|
||||
char: "n",
|
||||
description: "Database name",
|
||||
required: false,
|
||||
}),
|
||||
databaseName: Flags.string({
|
||||
description: "PostgreSQL database name",
|
||||
required: false,
|
||||
}),
|
||||
description: Flags.string({
|
||||
char: "d",
|
||||
description: "Database description",
|
||||
required: false,
|
||||
}),
|
||||
databasePassword: Flags.string({
|
||||
description: "Database password",
|
||||
required: false,
|
||||
}),
|
||||
databaseUser: Flags.string({
|
||||
description: "Database user",
|
||||
default: "postgres",
|
||||
}),
|
||||
dockerImage: Flags.string({
|
||||
description: "Docker image",
|
||||
default: "postgres:15",
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
appName: Flags.string({
|
||||
description: "App name",
|
||||
required: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabasePostgresCreate);
|
||||
let {
|
||||
projectId,
|
||||
environmentId,
|
||||
name,
|
||||
databaseName,
|
||||
description,
|
||||
databasePassword,
|
||||
databaseUser,
|
||||
dockerImage,
|
||||
appName
|
||||
} = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to create the PostgreSQL instance in:",
|
||||
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:",
|
||||
name: "environment",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
environmentId = environment.environmentId;
|
||||
}
|
||||
|
||||
if (!name || !databaseName || !appName || !databasePassword) {
|
||||
const dbDetails = await inquirer.prompt([
|
||||
{
|
||||
message: "Enter the name:",
|
||||
name: "name",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Database name is required"),
|
||||
default: name,
|
||||
},
|
||||
{
|
||||
message: "Database name:",
|
||||
name: "databaseName",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Database name is required"),
|
||||
default: databaseName,
|
||||
},
|
||||
{
|
||||
message: "Enter the database description (optional):",
|
||||
name: "description",
|
||||
type: "input",
|
||||
default: description,
|
||||
},
|
||||
{
|
||||
message: "Database password:",
|
||||
name: "databasePassword",
|
||||
type: "password",
|
||||
default: databasePassword,
|
||||
},
|
||||
{
|
||||
default: dockerImage || "postgres:15",
|
||||
message: "Docker Image (default: postgres:15):",
|
||||
name: "dockerImage",
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
default: databaseUser || "postgres",
|
||||
message: "Database User: (default: postgres):",
|
||||
name: "databaseUser",
|
||||
type: "input",
|
||||
},
|
||||
]);
|
||||
|
||||
name = dbDetails.name;
|
||||
databaseName = dbDetails.databaseName;
|
||||
description = dbDetails.description;
|
||||
databasePassword = dbDetails.databasePassword;
|
||||
dockerImage = dbDetails.dockerImage;
|
||||
databaseUser = dbDetails.databaseUser;
|
||||
|
||||
const appNamePrompt = await inquirer.prompt([
|
||||
{
|
||||
default: appName || `${slugify(name)}`,
|
||||
message: "Enter the App name:",
|
||||
name: "appName",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "App name is required"),
|
||||
},
|
||||
]);
|
||||
|
||||
appName = appNamePrompt.appName;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 PostgreSQL instance?',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirm.proceed) {
|
||||
this.error(chalk.yellow("PostgreSQL creation cancelled."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/postgres.create`,
|
||||
{
|
||||
json: {
|
||||
name,
|
||||
databaseName,
|
||||
description,
|
||||
databasePassword,
|
||||
databaseUser,
|
||||
dockerImage,
|
||||
appName,
|
||||
projectId,
|
||||
environmentId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
this.error(chalk.red("Error creating PostgreSQL instance", response.data.result.data.json));
|
||||
}
|
||||
|
||||
this.log(chalk.green(`PostgreSQL instance '${name}' created successfully.`));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error creating PostgreSQL instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
|
||||
import { readAuthConfig } from "../../../utils/utils.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.";
|
||||
|
||||
static examples = [
|
||||
"$ <%= config.bin %> postgres delete",
|
||||
"$ <%= config.bin %> postgres delete -p <projectId>",
|
||||
];
|
||||
|
||||
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,
|
||||
}),
|
||||
postgresId: Flags.string({
|
||||
char: "d",
|
||||
description: "ID of the PostgreSQL database",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation",
|
||||
required: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabasePostgresDelete);
|
||||
let { projectId, environmentId, postgresId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project.projectId,
|
||||
})),
|
||||
message: "Select a project to delete the PostgreSQL instance from:",
|
||||
name: "selectedProject",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
selectedProject = projects.find(p => p.projectId === answers.selectedProject);
|
||||
projectId = answers.selectedProject;
|
||||
} 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",
|
||||
},
|
||||
]);
|
||||
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([
|
||||
{
|
||||
choices: selectedEnvironment.postgres.map((db: Database) => ({
|
||||
name: db.name,
|
||||
value: db.postgresId,
|
||||
})),
|
||||
message: "Select the PostgreSQL instance to delete:",
|
||||
name: "selectedDb",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
postgresId = dbAnswers.selectedDb;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 PostgreSQL instance?",
|
||||
name: "confirmDelete",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDelete) {
|
||||
this.error(chalk.yellow("PostgreSQL deletion cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/postgres.remove`,
|
||||
{
|
||||
json: {
|
||||
postgresId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
this.error(chalk.red("Error deleting PostgreSQL instance"));
|
||||
}
|
||||
this.log(chalk.green("PostgreSQL instance deleted successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error deleting PostgreSQL instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import chalk from "chalk";
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import inquirer from "inquirer";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
import axios from "axios";
|
||||
|
||||
export default class DatabasePostgresDeploy extends Command {
|
||||
static description = "Deploy a PostgreSQL instance to a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> postgres 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,
|
||||
}),
|
||||
postgresId: Flags.string({
|
||||
char: "d",
|
||||
description: "ID of the PostgreSQL instance to deploy",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabasePostgresDeploy);
|
||||
let { projectId, environmentId, postgresId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to deploy the PostgreSQL instance 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:",
|
||||
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([
|
||||
{
|
||||
choices: selectedEnvironment.postgres.map((db: Database) => ({
|
||||
name: db.name,
|
||||
value: db.postgresId,
|
||||
})),
|
||||
message: "Select the PostgreSQL instance to deploy:",
|
||||
name: "selectedDb",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
postgresId = dbAnswers.selectedDb;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirmar si no se especifica --skipConfirm
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to deploy this PostgreSQL instance?",
|
||||
name: "confirmDeploy",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDeploy) {
|
||||
this.error(chalk.yellow("PostgreSQL deployment cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/postgres.deploy`,
|
||||
{
|
||||
json: {
|
||||
postgresId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error deploying PostgreSQL instance"));
|
||||
}
|
||||
this.log(chalk.green("PostgreSQL instance deployed successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error deploying PostgreSQL instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import chalk from "chalk";
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import inquirer from "inquirer";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
import axios from "axios";
|
||||
|
||||
export default class DatabasePostgresStop extends Command {
|
||||
static description = "Stop a PostgreSQL instance in a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> postgres 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,
|
||||
}),
|
||||
postgresId: Flags.string({
|
||||
char: "d",
|
||||
description: "ID of the PostgreSQL instance to stop",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabasePostgresStop);
|
||||
let { projectId, environmentId, postgresId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to stop the PostgreSQL instance 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:",
|
||||
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([
|
||||
{
|
||||
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
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to stop this PostgreSQL instance?",
|
||||
name: "confirmStop",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmStop) {
|
||||
this.error(chalk.yellow("PostgreSQL stop cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/postgres.stop`,
|
||||
{
|
||||
json: {
|
||||
postgresId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error stopping PostgreSQL instance"));
|
||||
}
|
||||
this.log(chalk.green("PostgreSQL instance stopped successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error stopping PostgreSQL instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
import { slugify } from "../../../utils/slug.js";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import { getProjects, type Database } from "../../../utils/shared.js";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
|
||||
export default class DatabaseRedisCreate extends Command {
|
||||
static description = "Create a new Redis instance within a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> redis create"];
|
||||
|
||||
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,
|
||||
}),
|
||||
name: Flags.string({
|
||||
char: "n",
|
||||
description: "Instance name",
|
||||
required: false,
|
||||
}),
|
||||
description: Flags.string({
|
||||
char: "d",
|
||||
description: "Instance description",
|
||||
required: false,
|
||||
}),
|
||||
databasePassword: Flags.string({
|
||||
description: "Redis password",
|
||||
required: false,
|
||||
}),
|
||||
dockerImage: Flags.string({
|
||||
description: "Docker image",
|
||||
default: "redis:7",
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
appName: Flags.string({
|
||||
description: "App name",
|
||||
required: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseRedisCreate);
|
||||
let {
|
||||
projectId,
|
||||
name,
|
||||
description,
|
||||
databasePassword,
|
||||
dockerImage,
|
||||
appName
|
||||
} = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to create the Redis instance in:",
|
||||
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:",
|
||||
name: "environment",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
environmentId = environment.environmentId;
|
||||
}
|
||||
|
||||
if (!name || !appName || !databasePassword) {
|
||||
const redisDetails = await inquirer.prompt([
|
||||
{
|
||||
message: "Enter the name:",
|
||||
name: "name",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Instance name is required"),
|
||||
default: name,
|
||||
},
|
||||
{
|
||||
message: "Enter the instance description (optional):",
|
||||
name: "description",
|
||||
type: "input",
|
||||
default: description,
|
||||
},
|
||||
{
|
||||
message: "Redis password:",
|
||||
name: "databasePassword",
|
||||
type: "password",
|
||||
default: databasePassword,
|
||||
},
|
||||
{
|
||||
default: dockerImage || "redis:7",
|
||||
message: "Docker Image (default: redis:7):",
|
||||
name: "dockerImage",
|
||||
type: "input",
|
||||
},
|
||||
]);
|
||||
|
||||
name = redisDetails.name;
|
||||
description = redisDetails.description;
|
||||
databasePassword = redisDetails.databasePassword;
|
||||
dockerImage = redisDetails.dockerImage;
|
||||
|
||||
const appNamePrompt = await inquirer.prompt([
|
||||
{
|
||||
default: appName || `${slugify(name)}`,
|
||||
message: "Enter the App name:",
|
||||
name: "appName",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "App name is required"),
|
||||
},
|
||||
]);
|
||||
|
||||
appName = appNamePrompt.appName;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 Redis instance?',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirm.proceed) {
|
||||
this.error(chalk.yellow("Redis creation cancelled."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/redis.create`,
|
||||
{
|
||||
json: {
|
||||
name,
|
||||
description,
|
||||
databasePassword,
|
||||
dockerImage,
|
||||
appName,
|
||||
projectId,
|
||||
environmentId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
this.error(chalk.red("Error creating Redis instance", response.data.result.data.json));
|
||||
}
|
||||
|
||||
this.log(chalk.green(`Redis instance '${name}' created successfully.`));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error creating Redis instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import chalk from "chalk";
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import inquirer from "inquirer";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
import axios from "axios";
|
||||
|
||||
export default class DatabaseRedisDelete extends Command {
|
||||
static description = "Delete a Redis instance from a project.";
|
||||
|
||||
static examples = [
|
||||
"$ <%= config.bin %> redis delete",
|
||||
"$ <%= config.bin %> redis delete -p <projectId>",
|
||||
];
|
||||
|
||||
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,
|
||||
}),
|
||||
redisId: Flags.string({
|
||||
char: "r",
|
||||
description: "ID of the Redis instance to delete",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseRedisDelete);
|
||||
let { projectId, environmentId, redisId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project.projectId,
|
||||
})),
|
||||
message: "Select a project to delete the Redis instance from:",
|
||||
name: "selectedProject",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
selectedProject = projects.find(p => p.projectId === answers.selectedProject);
|
||||
projectId = answers.selectedProject;
|
||||
} 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",
|
||||
},
|
||||
]);
|
||||
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([
|
||||
{
|
||||
choices: selectedEnvironment.redis.map((db: Database) => ({
|
||||
name: db.name,
|
||||
value: db.redisId,
|
||||
})),
|
||||
message: "Select the Redis instance to delete:",
|
||||
name: "selectedDb",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
redisId = dbAnswers.selectedDb;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 Redis instance?",
|
||||
name: "confirmDelete",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDelete) {
|
||||
this.error(chalk.yellow("Redis deletion cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/redis.remove`,
|
||||
{
|
||||
json: {
|
||||
redisId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
this.error(chalk.red("Error deleting Redis instance"));
|
||||
}
|
||||
this.log(chalk.green("Redis instance deleted successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error deleting Redis instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import chalk from "chalk";
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import inquirer from "inquirer";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
import axios from "axios";
|
||||
|
||||
export default class DatabaseRedisDeploy extends Command {
|
||||
static description = "Deploy a Redis instance to a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> redis 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,
|
||||
}),
|
||||
redisId: Flags.string({
|
||||
char: "r",
|
||||
description: "ID of the Redis instance to deploy",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseRedisDeploy);
|
||||
let { projectId, environmentId, redisId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to deploy the Redis instance 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:",
|
||||
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([
|
||||
{
|
||||
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
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to deploy this Redis instance?",
|
||||
name: "confirmDeploy",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmDeploy) {
|
||||
this.error(chalk.yellow("Redis deployment cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/redis.deploy`,
|
||||
{
|
||||
json: {
|
||||
redisId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error deploying Redis instance"));
|
||||
}
|
||||
this.log(chalk.green("Redis instance deployed successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error deploying Redis instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import { readAuthConfig } from "../../../utils/utils.js";
|
||||
import chalk from "chalk";
|
||||
import { getProject, getProjects, type Database } from "../../../utils/shared.js";
|
||||
import inquirer from "inquirer";
|
||||
import type { Answers } from "../../app/create.js";
|
||||
import axios from "axios";
|
||||
|
||||
export default class DatabaseRedisStop extends Command {
|
||||
static description = "Stop a Redis instance in a project.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> redis 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,
|
||||
}),
|
||||
redisId: Flags.string({
|
||||
char: "r",
|
||||
description: "ID of the Redis instance to stop",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(DatabaseRedisStop);
|
||||
let { projectId, environmentId, redisId } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
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<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select a project to stop the Redis instance 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:",
|
||||
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([
|
||||
{
|
||||
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
|
||||
if (!flags.skipConfirm) {
|
||||
const confirmAnswers = await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Are you sure you want to stop this Redis instance?",
|
||||
name: "confirmStop",
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmAnswers.confirmStop) {
|
||||
this.error(chalk.yellow("Redis stop cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/redis.stop`,
|
||||
{
|
||||
json: {
|
||||
redisId,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error stopping Redis instance"));
|
||||
}
|
||||
this.log(chalk.green("Redis instance stopped successfully."));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error stopping Redis instance: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
94
src/commands/env/pull.ts
vendored
94
src/commands/env/pull.ts
vendored
@@ -1,94 +0,0 @@
|
||||
import {Args, Command, Flags} from '@oclif/core'
|
||||
import {readAuthConfig} from "../../utils/utils.js";
|
||||
import chalk from "chalk";
|
||||
import {getProject, getProjects} from "../../utils/shared.js";
|
||||
import inquirer from "inquirer";
|
||||
import {Answers} from "../app/create.js";
|
||||
import fs from 'fs';
|
||||
|
||||
export default class EnvPull extends Command {
|
||||
static override args = {
|
||||
file: Args.string({description: 'write to file', required: true}),
|
||||
}
|
||||
|
||||
static override description = 'Store remote environment variables in local'
|
||||
|
||||
static override examples = [
|
||||
'<%= config.bin %> <%= command.id %> .env.stage.local',
|
||||
]
|
||||
|
||||
static override flags = {}
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const {args} = await this.parse(EnvPull)
|
||||
|
||||
if (fs.existsSync(args.file)) {
|
||||
const {override} = await inquirer.prompt<any>([
|
||||
{
|
||||
message: `Do you want to override ${args.file} file?`,
|
||||
name: "override",
|
||||
default: false,
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
if (!override) {
|
||||
return
|
||||
}
|
||||
}
|
||||
const auth = await readAuthConfig(this);
|
||||
console.log(chalk.blue.bold("\n Listing all Projects \n"));
|
||||
|
||||
const projects = await getProjects(auth, this);
|
||||
const {project} = await inquirer.prompt<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select the project:",
|
||||
name: "project",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
const projectId = project.projectId;
|
||||
const projectSelected = await getProject(projectId, auth, this);
|
||||
|
||||
const {environment} = await inquirer.prompt<any>([
|
||||
{
|
||||
choices: projectSelected.environments.map((environment: any) => ({
|
||||
name: environment.name,
|
||||
value: environment,
|
||||
})),
|
||||
message: "Select the environment:",
|
||||
name: "environment",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
|
||||
const choices = [
|
||||
...environment.applications.map((app: any) => ({
|
||||
name: `${app.name} (Application)`,
|
||||
value: app.env,
|
||||
})),
|
||||
...environment.compose.map((compose: any) => ({
|
||||
name: `${compose.name} (Compose)`,
|
||||
value: compose.env,
|
||||
})),
|
||||
]
|
||||
const {env} = await inquirer.prompt<any>([
|
||||
{
|
||||
choices,
|
||||
message: "Select a service to pull the environment variables:",
|
||||
name: "env",
|
||||
type: "list",
|
||||
},
|
||||
|
||||
]);
|
||||
|
||||
|
||||
fs.writeFileSync(args.file, env || "")
|
||||
this.log(chalk.green("Environment variable write to file successful."));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
143
src/commands/env/push.ts
vendored
143
src/commands/env/push.ts
vendored
@@ -1,143 +0,0 @@
|
||||
import {Args, Command, Flags} from '@oclif/core'
|
||||
import fs from "fs";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
import {readAuthConfig} from "../../utils/utils.js";
|
||||
import {getProject, getProjects} from "../../utils/shared.js";
|
||||
import {Answers} from "../app/create.js";
|
||||
import axios from "axios";
|
||||
|
||||
export default class EnvPush extends Command {
|
||||
static override args = {
|
||||
file: Args.string({description: '.env file to push', required: true}),
|
||||
}
|
||||
|
||||
static override description = 'Push dotenv file to remote service'
|
||||
|
||||
static override examples = [
|
||||
'<%= config.bin %> <%= command.id %> .env.stage.local',
|
||||
]
|
||||
|
||||
static override flags = {}
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const {args, flags} = await this.parse(EnvPush)
|
||||
|
||||
if (!fs.existsSync(args.file)) {
|
||||
console.log(chalk.red.bold(`\n File ${args.file} doesn't exists \n`));
|
||||
return;
|
||||
}
|
||||
|
||||
const {override} = await inquirer.prompt<any>([
|
||||
{
|
||||
message: `This command will override entire remote environment variables. Do you want to continue?`,
|
||||
name: "override",
|
||||
default: false,
|
||||
type: "confirm",
|
||||
},
|
||||
]);
|
||||
if (!override) {
|
||||
return
|
||||
}
|
||||
|
||||
const fileContent = fs.readFileSync(args.file, 'utf-8');
|
||||
const auth = await readAuthConfig(this);
|
||||
console.log(chalk.blue.bold("\n Listing all Projects \n"));
|
||||
|
||||
const projects = await getProjects(auth, this);
|
||||
const {project} = await inquirer.prompt<Answers>([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project,
|
||||
})),
|
||||
message: "Select the project:",
|
||||
name: "project",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
const projectId = project.projectId;
|
||||
const projectSelected = await getProject(projectId, auth, this);
|
||||
|
||||
const {environment} = await inquirer.prompt<any>([
|
||||
{
|
||||
choices: projectSelected.environments.map((environment: any) => ({
|
||||
name: environment.name,
|
||||
value: environment,
|
||||
})),
|
||||
message: "Select the environment:",
|
||||
name: "environment",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
|
||||
const choices = [
|
||||
...environment.applications.map((app: any) => ({
|
||||
name: `${app.name} (Application)`,
|
||||
value: {serviceType: 'app', service: app},
|
||||
})),
|
||||
...environment.compose.map((compose: any) => ({
|
||||
name: `${compose.name} (Compose)`,
|
||||
value: {serviceType: 'compose', service: compose}
|
||||
})),
|
||||
]
|
||||
const {result: {serviceType, service}} = await inquirer.prompt<any>([
|
||||
{
|
||||
choices,
|
||||
message: "Select a service to push the environment variables:",
|
||||
name: "result",
|
||||
type: "list",
|
||||
},
|
||||
|
||||
]);
|
||||
|
||||
if (serviceType === 'app') {
|
||||
const {applicationId} = service;
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/application.update`,
|
||||
{
|
||||
json: {
|
||||
applicationId,
|
||||
env: fileContent
|
||||
}
|
||||
}, {
|
||||
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
)
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error stopping application"));
|
||||
}
|
||||
this.log(chalk.green("Environment variable push successful."));
|
||||
|
||||
}
|
||||
|
||||
if (serviceType === 'compose') {
|
||||
const {composeId} = service;
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/compose.update`,
|
||||
{
|
||||
json: {
|
||||
composeId,
|
||||
env: fileContent
|
||||
}
|
||||
}, {
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
)
|
||||
if (response.status !== 200) {
|
||||
this.error(chalk.red("Error stopping application"));
|
||||
}
|
||||
this.log(chalk.green("Environment variable push successful."));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
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<void> {
|
||||
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<Answers>([
|
||||
{
|
||||
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}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
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 <projectId>",
|
||||
];
|
||||
|
||||
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<void> {
|
||||
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<Answers>([
|
||||
{
|
||||
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}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
import { readAuthConfig } from "../../utils/utils.js";
|
||||
|
||||
export default class ProjectCreate extends Command {
|
||||
static description = "Create a new project.";
|
||||
|
||||
static examples = [
|
||||
"$ <%= config.bin %> project create",
|
||||
"$ <%= config.bin %> project create -n MyProject -d 'Project description'",
|
||||
"$ <%= config.bin %> project create --name MyProject --skipConfirm",
|
||||
];
|
||||
|
||||
static flags = {
|
||||
name: Flags.string({
|
||||
char: "n",
|
||||
description: "Name of the project",
|
||||
required: false,
|
||||
}),
|
||||
description: Flags.string({
|
||||
char: "d",
|
||||
description: "Description of the project",
|
||||
required: false,
|
||||
}),
|
||||
skipConfirm: Flags.boolean({
|
||||
char: "y",
|
||||
description: "Skip confirmation prompt",
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
const { flags } = await this.parse(ProjectCreate);
|
||||
let { name, description } = flags;
|
||||
|
||||
// Modo interactivo si no se proporcionan los flags necesarios
|
||||
if (!name) {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
message: "Enter the project name:",
|
||||
name: "name",
|
||||
type: "input",
|
||||
validate: (input) => (input ? true : "Project name is required"),
|
||||
},
|
||||
{
|
||||
message: "Enter the project description (optional):",
|
||||
name: "description",
|
||||
type: "input",
|
||||
default: description || "",
|
||||
},
|
||||
]);
|
||||
|
||||
name = answers.name;
|
||||
description = answers.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 project?',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirm.proceed) {
|
||||
this.error(chalk.yellow("Project creation cancelled."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
const response = await axios.post(
|
||||
`${auth.url}/api/trpc/project.create`,
|
||||
{
|
||||
json: {
|
||||
name,
|
||||
description,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
this.error(chalk.red("Error creating project", response.data.result.data.json));
|
||||
}
|
||||
|
||||
this.log(chalk.green(`Project '${name}' created successfully.`));
|
||||
} catch (error: any) {
|
||||
this.error(chalk.red(`Error creating project: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
import { Command, Flags } from "@oclif/core";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
|
||||
import { readAuthConfig } from "../../utils/utils.js";
|
||||
import { getProjects } from "../../utils/shared.js";
|
||||
|
||||
export default class ProjectInfo extends Command {
|
||||
static description =
|
||||
"Get detailed information about a project, including the number of applications and databases.";
|
||||
|
||||
static examples = [
|
||||
"$ <%= config.bin %> project info",
|
||||
"$ <%= config.bin %> project info -p <projectId>",
|
||||
];
|
||||
|
||||
static flags = {
|
||||
projectId: Flags.string({
|
||||
char: "p",
|
||||
description: "ID of the project",
|
||||
required: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
|
||||
const { flags } = await this.parse(ProjectInfo);
|
||||
|
||||
if (flags.projectId) {
|
||||
await this.showProjectInfo(auth, flags.projectId);
|
||||
} else {
|
||||
console.log(chalk.blue.bold("\n Listing all Projects \n"));
|
||||
|
||||
try {
|
||||
const projects = await getProjects(auth, this);
|
||||
|
||||
if (projects.length === 0) {
|
||||
this.log(chalk.yellow("No projects found."));
|
||||
return;
|
||||
}
|
||||
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
choices: projects.map((project) => ({
|
||||
name: project.name,
|
||||
value: project.projectId,
|
||||
})),
|
||||
message: "Select a project to view details:",
|
||||
name: "selectedProject",
|
||||
type: "list",
|
||||
},
|
||||
]);
|
||||
|
||||
const selectedProjectId = answers.selectedProject;
|
||||
|
||||
await this.showProjectInfo(auth, selectedProjectId);
|
||||
} catch (error) {
|
||||
// @ts-expect-error hola
|
||||
this.error(chalk.red(`Failed to fetch project list: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async showProjectInfo(
|
||||
auth: { token: string; url: string },
|
||||
projectId: string,
|
||||
) {
|
||||
console.log(
|
||||
chalk.blue.bold(`\n Information for Project ID: ${projectId} \n`),
|
||||
);
|
||||
|
||||
try {
|
||||
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(
|
||||
chalk.green(
|
||||
`Description: ${projectInfo?.description || "No description"}`,
|
||||
),
|
||||
);
|
||||
|
||||
// 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."));
|
||||
}
|
||||
|
||||
// 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}`));
|
||||
|
||||
} catch (error) {
|
||||
this.error(
|
||||
// @ts-expect-error
|
||||
chalk.red(`Failed to fetch project information: ${error.message}`),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { Command } from "@oclif/core";
|
||||
import chalk from "chalk";
|
||||
import Table from "cli-table3";
|
||||
|
||||
import { readAuthConfig } from "../../utils/utils.js";
|
||||
import { getProjects } from "../../utils/shared.js";
|
||||
|
||||
export default class ProjectList extends Command {
|
||||
static description = "List all projects.";
|
||||
|
||||
static examples = ["$ <%= config.bin %> project list"];
|
||||
|
||||
public async run(): Promise<void> {
|
||||
const auth = await readAuthConfig(this);
|
||||
|
||||
console.log(chalk.blue.bold("\n Listing all Projects \n"));
|
||||
|
||||
try {
|
||||
const projects = await getProjects(auth, this);
|
||||
|
||||
if (projects.length === 0) {
|
||||
this.log(chalk.yellow("No projects found."));
|
||||
} else {
|
||||
this.log(chalk.green("Projects:"));
|
||||
const table = new Table({
|
||||
colWidths: [10, 30, 50],
|
||||
head: [
|
||||
chalk.cyan("Index"),
|
||||
chalk.cyan("Name"),
|
||||
chalk.cyan("Description"),
|
||||
],
|
||||
});
|
||||
const index = 1;
|
||||
for (const project of projects) {
|
||||
table.push([
|
||||
chalk.white(index + 1),
|
||||
chalk.white(project.name),
|
||||
chalk.gray(project.description || "No description"),
|
||||
]);
|
||||
}
|
||||
|
||||
this.log(table.toString());
|
||||
}
|
||||
} catch (error) {
|
||||
// @ts-expect-error error is not defined
|
||||
this.error(chalk.red(`Failed to list projects: ${error?.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import { Command } from "@oclif/core";
|
||||
import axios from "axios";
|
||||
import chalk from "chalk";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const configPath = path.join(__dirname, "..", "..", "config.json");
|
||||
|
||||
export default class Verify extends Command {
|
||||
static description = "Verify if the saved authentication token is valid";
|
||||
|
||||
static examples = ["$ <%= config.bin %> <%= command.id %>"];
|
||||
|
||||
async run() {
|
||||
console.log(chalk.blue.bold("\nVerifying Authentication Token"));
|
||||
|
||||
let token: string;
|
||||
let url: string;
|
||||
|
||||
// Verificar variables de entorno primero
|
||||
const envToken = process.env.DOKPLOY_AUTH_TOKEN;
|
||||
const envUrl = process.env.DOKPLOY_URL;
|
||||
|
||||
if (envToken && envUrl) {
|
||||
token = envToken;
|
||||
url = envUrl;
|
||||
this.log(chalk.green("Using environment variables for authentication"));
|
||||
} else {
|
||||
// Si no hay variables de entorno, verificar archivo de configuración
|
||||
if (!fs.existsSync(configPath)) {
|
||||
this.error(
|
||||
chalk.red(
|
||||
"No configuration found. Please either:\n" +
|
||||
"1. Authenticate using `authenticate` command\n" +
|
||||
"2. Set DOKPLOY_URL and DOKPLOY_AUTH_TOKEN environment variables",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
||||
token = config.token;
|
||||
url = config.url;
|
||||
this.log(chalk.green("Using configuration file for authentication"));
|
||||
} catch (error) {
|
||||
this.error(
|
||||
chalk.red(
|
||||
"Invalid configuration file. Please authenticate again using `authenticate` command.",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validar el token contra el servidor
|
||||
try {
|
||||
console.log(chalk.blue("Validating token with server..."));
|
||||
|
||||
const response = await axios.get(
|
||||
`${url}/api/trpc/user.get`,
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.data.result.data.json) {
|
||||
this.log(chalk.green("\n✓ Token is valid"));
|
||||
} else {
|
||||
this.error(
|
||||
chalk.red(
|
||||
"Invalid token. Please authenticate again using `authenticate` command.",
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
this.error(
|
||||
chalk.red(
|
||||
`Failed to verify token: ${error.message}. Please authenticate again using 'authenticate' command.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
8720
src/generated/commands.ts
Normal file
8720
src/generated/commands.ts
Normal file
File diff suppressed because it is too large
Load Diff
31
src/index.ts
31
src/index.ts
@@ -1 +1,30 @@
|
||||
export {run} from '@oclif/core'
|
||||
#!/usr/bin/env node
|
||||
|
||||
import chalk from "chalk";
|
||||
import { program } from "commander";
|
||||
import { registerAuthCommand } from "./commands/auth.js";
|
||||
import { registerGeneratedCommands } from "./generated/commands.js";
|
||||
|
||||
const pkg = {
|
||||
name: "dokploy",
|
||||
version: "0.3.0",
|
||||
description: "Dokploy CLI - Manage your Dokploy server",
|
||||
};
|
||||
|
||||
program
|
||||
.name(pkg.name)
|
||||
.version(pkg.version)
|
||||
.description(pkg.description)
|
||||
.action(() => {
|
||||
program.help();
|
||||
});
|
||||
|
||||
registerAuthCommand(program);
|
||||
registerGeneratedCommands(program);
|
||||
|
||||
const argv = process.argv.filter((arg) => arg !== "--");
|
||||
|
||||
program.parseAsync(argv).catch((err) => {
|
||||
console.error(chalk.red(err.message));
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Dokploy CLI",
|
||||
};
|
||||
@@ -1,114 +0,0 @@
|
||||
import type { Command } from "@oclif/core";
|
||||
|
||||
import axios from "axios";
|
||||
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 (
|
||||
auth: AuthConfig,
|
||||
command: Command,
|
||||
): Promise<Project[]> => {
|
||||
try {
|
||||
const response = await axios.get(`${auth.url}/api/trpc/project.all`, {
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
command.error(chalk.red("Error fetching projects"));
|
||||
}
|
||||
|
||||
const projects = response.data.result.data.json;
|
||||
|
||||
if (projects.length === 0) {
|
||||
command.log(chalk.yellow("No projects found."));
|
||||
return [];
|
||||
}
|
||||
|
||||
return projects;
|
||||
} catch (error) {
|
||||
// @ts-expect-error TODO: Fix this
|
||||
command.error(chalk.red(`Failed to fetch project list: ${error.message}`));
|
||||
}
|
||||
};
|
||||
|
||||
export const getProject = async (
|
||||
projectId: string | undefined,
|
||||
auth: AuthConfig,
|
||||
command: Command,
|
||||
) => {
|
||||
try {
|
||||
if (!projectId) {
|
||||
command.error(chalk.red("Project ID is required"));
|
||||
}
|
||||
const response = await axios.get(`${auth.url}/api/trpc/project.one`, {
|
||||
headers: {
|
||||
"x-api-key": auth.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
params: {
|
||||
input: JSON.stringify({
|
||||
json: { projectId },
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.data.result.data.json) {
|
||||
command.error(chalk.red("Error fetching project"));
|
||||
}
|
||||
|
||||
const project = response.data.result.data.json;
|
||||
|
||||
if (!project) {
|
||||
command.error(chalk.red("Error fetching project"));
|
||||
}
|
||||
|
||||
return project;
|
||||
} catch (error) {
|
||||
// @ts-expect-error TODO: Fix this
|
||||
command.error(chalk.red(`Failed to fetch project: ${error.message}`));
|
||||
}
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
import slug from "./slugify.js";
|
||||
|
||||
export const slugify = (text: string | undefined) => {
|
||||
if (!text) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const cleanedText = text.trim().replaceAll(/[^\d\sA-Za-z]/g, "");
|
||||
return slug(cleanedText, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
trim: true,
|
||||
});
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
import slugify from "slugify";
|
||||
|
||||
export default slugify as unknown as typeof slugify.default;
|
||||
@@ -1,48 +0,0 @@
|
||||
import type { Command } from "@oclif/core";
|
||||
|
||||
import chalk from "chalk";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const configPath = path.join(__dirname, "..", "..", "config.json");
|
||||
|
||||
export type AuthConfig = {
|
||||
token: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export const readAuthConfig = async (command: Command): Promise<AuthConfig> => {
|
||||
// Primero intentar leer desde variables de entorno
|
||||
const envToken = process.env.DOKPLOY_AUTH_TOKEN;
|
||||
const envUrl = process.env.DOKPLOY_URL;
|
||||
|
||||
if (envToken && envUrl) {
|
||||
return { token: envToken, url: envUrl };
|
||||
}
|
||||
|
||||
// Si no hay variables de entorno, usar el archivo de configuración
|
||||
if (!fs.existsSync(configPath)) {
|
||||
command.error(
|
||||
chalk.red(
|
||||
"No configuration file found and no environment variables set. Please authenticate first using the 'authenticate' command or set DOKPLOY_URL and DOKPLOY_AUTH_TOKEN environment variables.",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const configFileContent = fs.readFileSync(configPath, "utf8");
|
||||
const config = JSON.parse(configFileContent);
|
||||
const { token, url } = config;
|
||||
|
||||
if (!url || !token) {
|
||||
command.error(
|
||||
chalk.red(
|
||||
"Incomplete authentication details. Please authenticate again using the 'authenticate' command or set environment variables.",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return { token, url };
|
||||
};
|
||||
86
tests/cli.test.ts
Normal file
86
tests/cli.test.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(__dirname, "..");
|
||||
const CLI = path.join(ROOT, "dist", "index.js");
|
||||
|
||||
function run(...args: string[]): string {
|
||||
return execFileSync("node", [CLI, ...args], {
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, NO_COLOR: "1" },
|
||||
});
|
||||
}
|
||||
|
||||
describe("CLI", () => {
|
||||
it("should show help with --help", () => {
|
||||
const output = run("--help");
|
||||
expect(output).toContain("Dokploy CLI");
|
||||
expect(output).toContain("auth");
|
||||
expect(output).toContain("application");
|
||||
expect(output).toContain("project");
|
||||
});
|
||||
|
||||
it("should show version with --version", () => {
|
||||
const output = run("--version");
|
||||
expect(output.trim()).toMatch(/^\d+\.\d+\.\d+$/);
|
||||
});
|
||||
|
||||
it("should show subcommands for application", () => {
|
||||
const output = run("application", "--help");
|
||||
expect(output).toContain("create");
|
||||
expect(output).toContain("deploy");
|
||||
expect(output).toContain("delete");
|
||||
expect(output).toContain("stop");
|
||||
});
|
||||
|
||||
it("should show subcommands for postgres", () => {
|
||||
const output = run("postgres", "--help");
|
||||
expect(output).toContain("create");
|
||||
expect(output).toContain("deploy");
|
||||
expect(output).toContain("remove");
|
||||
});
|
||||
|
||||
it("should show options for a specific command", () => {
|
||||
const output = run("application", "create", "--help");
|
||||
expect(output).toContain("--name");
|
||||
expect(output).toContain("--environmentId");
|
||||
});
|
||||
|
||||
it("should show auth command options", () => {
|
||||
const output = run("auth", "--help");
|
||||
expect(output).toContain("--url");
|
||||
expect(output).toContain("--token");
|
||||
});
|
||||
|
||||
it("should show all expected command groups", () => {
|
||||
const output = run("--help");
|
||||
const expectedGroups = [
|
||||
"application",
|
||||
"postgres",
|
||||
"mysql",
|
||||
"redis",
|
||||
"mongo",
|
||||
"mariadb",
|
||||
"compose",
|
||||
"docker",
|
||||
"project",
|
||||
"server",
|
||||
"domain",
|
||||
"backup",
|
||||
"settings",
|
||||
"user",
|
||||
"environment",
|
||||
];
|
||||
for (const group of expectedGroups) {
|
||||
expect(output).toContain(group);
|
||||
}
|
||||
});
|
||||
|
||||
it("should exit with 0 when no args provided", () => {
|
||||
const output = run();
|
||||
expect(output).toContain("Usage:");
|
||||
});
|
||||
});
|
||||
56
tests/client.test.ts
Normal file
56
tests/client.test.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("readAuthConfig", () => {
|
||||
const originalEnv = { ...process.env };
|
||||
|
||||
beforeEach(() => {
|
||||
delete process.env.DOKPLOY_URL;
|
||||
delete process.env.DOKPLOY_API_KEY;
|
||||
delete process.env.DOKPLOY_AUTH_TOKEN;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = { ...originalEnv };
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("should read from DOKPLOY_API_KEY env var", async () => {
|
||||
process.env.DOKPLOY_URL = "https://test.dokploy.com";
|
||||
process.env.DOKPLOY_API_KEY = "test-key-123";
|
||||
|
||||
const { readAuthConfig } = await import("../src/client.js");
|
||||
const config = readAuthConfig();
|
||||
|
||||
expect(config.url).toBe("https://test.dokploy.com");
|
||||
expect(config.token).toBe("test-key-123");
|
||||
});
|
||||
|
||||
it("should read from DOKPLOY_AUTH_TOKEN env var as fallback", async () => {
|
||||
process.env.DOKPLOY_URL = "https://test.dokploy.com";
|
||||
process.env.DOKPLOY_AUTH_TOKEN = "auth-token-456";
|
||||
|
||||
const { readAuthConfig } = await import("../src/client.js");
|
||||
const config = readAuthConfig();
|
||||
|
||||
expect(config.url).toBe("https://test.dokploy.com");
|
||||
expect(config.token).toBe("auth-token-456");
|
||||
});
|
||||
|
||||
it("should prefer DOKPLOY_API_KEY over DOKPLOY_AUTH_TOKEN", async () => {
|
||||
process.env.DOKPLOY_URL = "https://test.dokploy.com";
|
||||
process.env.DOKPLOY_API_KEY = "api-key";
|
||||
process.env.DOKPLOY_AUTH_TOKEN = "auth-token";
|
||||
|
||||
const { readAuthConfig } = await import("../src/client.js");
|
||||
const config = readAuthConfig();
|
||||
|
||||
expect(config.token).toBe("api-key");
|
||||
});
|
||||
});
|
||||
|
||||
describe("saveAuthConfig", () => {
|
||||
it("should write config with correct structure", async () => {
|
||||
const { saveAuthConfig } = await import("../src/client.js");
|
||||
expect(typeof saveAuthConfig).toBe("function");
|
||||
});
|
||||
});
|
||||
63
tests/generator.test.ts
Normal file
63
tests/generator.test.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(__dirname, "..");
|
||||
|
||||
describe("generator", () => {
|
||||
const specPath = path.join(ROOT, "openapi.json");
|
||||
const generatedPath = path.join(ROOT, "src", "generated", "commands.ts");
|
||||
|
||||
it("openapi.json should exist", () => {
|
||||
expect(fs.existsSync(specPath)).toBe(true);
|
||||
});
|
||||
|
||||
it("generated commands file should exist", () => {
|
||||
expect(fs.existsSync(generatedPath)).toBe(true);
|
||||
});
|
||||
|
||||
it("generated file should export registerGeneratedCommands", () => {
|
||||
const content = fs.readFileSync(generatedPath, "utf8");
|
||||
expect(content).toContain("export function registerGeneratedCommands");
|
||||
});
|
||||
|
||||
it("generated file should import from client", () => {
|
||||
const content = fs.readFileSync(generatedPath, "utf8");
|
||||
expect(content).toContain('from "../client.js"');
|
||||
});
|
||||
|
||||
it("generated file should contain command groups from the spec", () => {
|
||||
const spec = JSON.parse(fs.readFileSync(specPath, "utf8"));
|
||||
const content = fs.readFileSync(generatedPath, "utf8");
|
||||
|
||||
// Only check groups that have a dot-separated action (group.action)
|
||||
const groups = new Set<string>();
|
||||
for (const p of Object.keys(spec.paths)) {
|
||||
const clean = p.replace(/^\//, "");
|
||||
const [group, ...rest] = clean.split(".");
|
||||
if (group && rest.length > 0) groups.add(group);
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
expect(content).toContain(`"${group}.`);
|
||||
}
|
||||
});
|
||||
|
||||
it("number of apiPost/apiGet calls should match valid endpoints", () => {
|
||||
const spec = JSON.parse(fs.readFileSync(specPath, "utf8"));
|
||||
const content = fs.readFileSync(generatedPath, "utf8");
|
||||
|
||||
// Count only endpoints with group.action pattern
|
||||
let validEndpoints = 0;
|
||||
for (const p of Object.keys(spec.paths)) {
|
||||
const clean = p.replace(/^\//, "");
|
||||
const [group, ...rest] = clean.split(".");
|
||||
if (group && rest.length > 0) validEndpoints++;
|
||||
}
|
||||
|
||||
const apiCalls = (content.match(/await api(Post|Get)\(/g) || []).length;
|
||||
expect(apiCalls).toBe(validEndpoints);
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@
|
||||
"strict": true,
|
||||
"target": "es2022",
|
||||
"moduleResolution": "node16",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["./src/**/*"],
|
||||
"ts-node": {
|
||||
|
||||
1
tsconfig.tsbuildinfo
Normal file
1
tsconfig.tsbuildinfo
Normal file
@@ -0,0 +1 @@
|
||||
{"root":["./src/client.ts","./src/index.ts","./src/commands/auth.ts","./src/generated/commands.ts"],"version":"5.9.3"}
|
||||
Reference in New Issue
Block a user