@launchframe/cli 1.0.0-beta.26 → 1.0.0-beta.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@launchframe/cli",
3
- "version": "1.0.0-beta.26",
3
+ "version": "1.0.0-beta.27",
4
4
  "description": "Production-ready B2B SaaS boilerplate with subscriptions, credits, and multi-tenancy",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -41,6 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "chalk": "^4.1.2",
44
+ "dotenv": "^17.3.1",
44
45
  "fs-extra": "^11.1.1",
45
46
  "inquirer": "^8.2.5"
46
47
  }
@@ -0,0 +1,84 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const inquirer = require('inquirer');
5
+ const { spawnSync } = require('child_process');
6
+ const { requireProject, getProjectConfig } = require('../utils/project-helpers');
7
+
8
+ async function databaseConsole({ remote = false } = {}) {
9
+ requireProject();
10
+
11
+ const infrastructurePath = path.join(process.cwd(), 'infrastructure');
12
+
13
+ if (!fs.existsSync(infrastructurePath)) {
14
+ console.error(chalk.red('\n❌ Error: infrastructure/ directory not found'));
15
+ console.log(chalk.gray('Make sure you are in the root of your LaunchFrame project.\n'));
16
+ process.exit(1);
17
+ }
18
+
19
+ if (remote) {
20
+ // 1. Check deployment is configured
21
+ const config = getProjectConfig();
22
+
23
+ if (!config.deployConfigured || !config.deployment) {
24
+ console.error(chalk.red('\n❌ Deployment is not configured.'));
25
+ console.log(chalk.gray('Run deploy:configure first.\n'));
26
+ process.exit(1);
27
+ }
28
+
29
+ const { vpsUser, vpsHost, vpsAppFolder } = config.deployment;
30
+
31
+ // 2. Warn before connecting to production
32
+ console.log(chalk.yellow.bold('\n⚠️ You are about to connect to the PRODUCTION database.\n'));
33
+ console.log(chalk.gray(` Host: ${vpsHost}`));
34
+ console.log(chalk.gray(` Folder: ${vpsAppFolder}\n`));
35
+
36
+ const { confirmed } = await inquirer.prompt([
37
+ {
38
+ type: 'confirm',
39
+ name: 'confirmed',
40
+ message: 'Are you sure you want to open a console to the production database?',
41
+ default: false
42
+ }
43
+ ]);
44
+
45
+ if (!confirmed) {
46
+ console.log(chalk.gray('\nAborted.\n'));
47
+ process.exit(0);
48
+ }
49
+
50
+ console.log(chalk.blue.bold('\n🔌 Connecting to production database...\n'));
51
+
52
+ // 3. Let the shell inside the container expand $POSTGRES_USER / $POSTGRES_DB.
53
+ // Pass the remote command as a single ssh argument (spawnSync array form)
54
+ // so the local shell never touches it.
55
+ const remoteCmd = `cd ${vpsAppFolder}/infrastructure && docker compose -f docker-compose.yml -f docker-compose.prod.yml exec -it database sh -c 'psql -U $POSTGRES_USER $POSTGRES_DB'`;
56
+
57
+ const result = spawnSync('ssh', ['-t', `${vpsUser}@${vpsHost}`, remoteCmd], { stdio: 'inherit' });
58
+
59
+ if (result.status !== 0) {
60
+ console.error(chalk.red('\n❌ Could not connect to the production database.'));
61
+ console.log(chalk.gray('Check that the VPS is reachable and services are running.\n'));
62
+ process.exit(1);
63
+ }
64
+ } else {
65
+ console.log(chalk.blue.bold('\n🗄️ Opening local database console...\n'));
66
+
67
+ // Let the shell inside the container expand $POSTGRES_USER / $POSTGRES_DB
68
+ const psqlCmd = [
69
+ 'compose', '-f', 'docker-compose.yml', '-f', 'docker-compose.dev.yml',
70
+ 'exec', 'database', 'sh', '-c', 'psql -U $POSTGRES_USER $POSTGRES_DB'
71
+ ];
72
+
73
+ const result = spawnSync('docker', psqlCmd, { cwd: infrastructurePath, stdio: 'inherit' });
74
+
75
+ if (result.status !== 0) {
76
+ console.error(chalk.red('\n❌ Could not connect to the local database container.'));
77
+ console.log(chalk.gray('Make sure services are running:'));
78
+ console.log(chalk.white(' launchframe docker:up\n'));
79
+ process.exit(1);
80
+ }
81
+ }
82
+ }
83
+
84
+ module.exports = { databaseConsole };
@@ -41,6 +41,9 @@ function help() {
41
41
  console.log(chalk.gray(' migration:run Run pending database migrations'));
42
42
  console.log(chalk.gray(' migration:create Create new database migration'));
43
43
  console.log(chalk.gray(' migration:revert Revert last database migration\n'));
44
+ console.log(chalk.white('Database:'));
45
+ console.log(chalk.gray(' database:console Open a PostgreSQL console (local)'));
46
+ console.log(chalk.gray(' --remote Connect to the production database\n'));
44
47
 
45
48
  console.log(chalk.white('Service Management:'));
46
49
  console.log(chalk.gray(' service:add <name> Add an optional service to your project'));
package/src/index.js CHANGED
@@ -32,6 +32,7 @@ const { dockerLogs } = require('./commands/docker-logs');
32
32
  const { migrateRun } = require('./commands/migration-run');
33
33
  const { migrateCreate } = require('./commands/migration-create');
34
34
  const { migrateRevert } = require('./commands/migration-revert');
35
+ const { databaseConsole } = require('./commands/database-console');
35
36
  const { dockerDestroy } = require('./commands/docker-destroy');
36
37
  const { doctor } = require('./commands/doctor');
37
38
  const { help } = require('./commands/help');
@@ -160,6 +161,9 @@ async function main() {
160
161
  case 'migration:revert':
161
162
  await migrateRevert();
162
163
  break;
164
+ case 'database:console':
165
+ await databaseConsole({ remote: flags.remote });
166
+ break;
163
167
  case 'doctor':
164
168
  await doctor();
165
169
  break;