@seip/blue-bird 0.7.4 → 0.7.6

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/.env_example CHANGED
@@ -21,16 +21,14 @@ REDIS_PASSWORD=""
21
21
  PM2_INSTANCES=1
22
22
 
23
23
  # Database Configuration (Used only for local development outside Docker)
24
- # SQLite (Default)
25
- DATABASE_URL="file:./dev.db"
26
-
27
- # MySQL (Uncomment to use MySQL locally or DEV)
28
- # DATABASE_URL="mysql://root:root@localhost:3306/blue_bird"
24
+ # MySQL
25
+ DATABASE_URL="mysql://root:root@localhost:3306/blue_bird"
29
26
 
30
27
  # PostgreSQL (Uncomment to use PostgreSQL locally or DEV)
31
- # DATABASE_URL="postgresql://root:root@localhost:5432/blue_bird?schema=public"
28
+ # DATABASE_URL="postgresql://postgres:root@localhost:5432/blue_bird?schema=public"
32
29
 
33
30
  # Database / Docker Configuration
31
+ DB_TYPE="mysql"
34
32
  DB_NAME="blue_bird"
35
33
  DB_USER="root"
36
34
  DB_PASSWORD="root"
package/AGENTS.md CHANGED
@@ -156,31 +156,34 @@ apiRouter.use(App.helmet());
156
156
 
157
157
  ## 8. Docker Compose CLI
158
158
 
159
- Blue Bird features a built-in Docker Compose CLI wrapper to deploy and manage containerized development databases and production stacks.
159
+ Blue Bird features a built-in Docker Compose CLI wrapper (`core/cli/docker.js`) to deploy and manage containerized development databases and production stacks across MySQL, PostgreSQL, or no-database (`none`) architectures.
160
160
 
161
161
  Production deployments always use Docker for orchestration, running:
162
162
  - Nginx: Serves static files directly from `frontend/dist/client/` and blocks common scanner requests (`.env`, `.git`, etc.) with fallback to Express.
163
163
  - Node.js App: Managed via PM2 in cluster mode using `PM2_INSTANCES` configuration (defaults to `1`, can be set to `max`).
164
- - MySQL: Database service.
164
+ - Database: MySQL (`mysql:8.0`) or PostgreSQL (`postgres:18-alpine`), dynamically detected via `getDbType()` reading `DB_TYPE` / `DATABASE_URL` from `.env`.
165
165
  - Redis: Memory caching and session store.
166
166
 
167
167
  ```bash
168
168
  # Manage containers using blue-bird CLI
169
- npx blue-bird docker start # Starts production app stack (mysql, redis, app, nginx)
170
- npx blue-bird docker start mysql # Starts MySQL container only (useful for local development)
169
+ npx blue-bird docker start # Starts production app stack (DB, redis, app, nginx)
170
+ npx blue-bird docker start db # Starts configured database container only (postgres or mysql)
171
171
  npx blue-bird docker start redis # Starts Redis container only
172
- npx blue-bird docker start dbs # Starts both database containers (MySQL + Redis)
172
+ npx blue-bird docker start dbs # Starts both database containers (configured DB + Redis)
173
173
  npx blue-bird docker stop # Stops all running containers
174
174
  npx blue-bird docker build # Builds/rebuilds application image
175
175
  npx blue-bird docker ps # Shows status of active containers
176
176
  npx blue-bird docker logs # Tails Node.js app container logs
177
+ npx blue-bird docker logs db # Tails configured database container logs
177
178
  npx blue-bird docker pm2 [args] # Runs PM2 commands inside the app container (e.g. status, monit)
178
- npx blue-bird docker mysql # Runs interactive MySQL client terminal inside the container
179
- npx blue-bird docker redis # Runs interactive Redis client terminal inside the container
179
+ npx blue-bird docker db # Runs interactive shell inside container (psql for Postgres, mysql for MySQL)
180
+ npx blue-bird docker psql # Runs interactive PostgreSQL client terminal inside container
181
+ npx blue-bird docker mysql # Runs interactive MySQL client terminal inside container
182
+ npx blue-bird docker redis # Runs interactive Redis client terminal inside container
180
183
  npx blue-bird docker prune # Cleans unused volumes, dangling images, and BuildKit caches
181
184
  ```
182
185
 
183
- The container names and virtual networks are namespaced by the `TITLE` environment variable parsed from `.env` to prevent resource collisions on VPS hosts. Alternatively, PM2 and other services can be run manually in standalone server environments.
186
+ The container names and virtual networks are namespaced by the `TITLE` environment variable parsed from `.env` to prevent resource collisions on VPS hosts. Alternatively, PM2 and other services can be run manually in standalone server environments by configuring `DATABASE_URL` inside `.env`.
184
187
 
185
188
  ## 9. AI Development Guidelines
186
189
 
@@ -191,18 +194,23 @@ The container names and virtual networks are namespaced by the `TITLE` environme
191
194
 
192
195
  ## 10. Database Module (database.js)
193
196
 
194
- Blue Bird provides a unified wrapper class for MySQL databases via `mysql2` connections pool with automatic retries and built-in query caching:
197
+ Blue Bird provides a unified wrapper class (`core/database.js`) supporting **MySQL (`mysql2/promise`)** and **PostgreSQL (`pg`)**. It features connection pooling, automatic retries on startup, query formatting, and built-in Redis query caching:
198
+
199
+ - **Dynamic Initialization:** When `npx blue-bird` (`core/cli/init.js`) runs, it prompts the developer for the database type (`none`, `mysql`, `postgres`). It then intelligently copies the correct `docker-compose.yml` template (`docker-compose.mysql.yml`, `docker-compose.postgres.yml`, or `docker-compose.none.yml`) and configures `.env` with `DB_TYPE` and `DATABASE_URL`.
200
+ - **Parameter Placeholders:** When using `pg` for PostgreSQL with `connection.query()`, `?` placeholders are automatically translated to `$1, $2, ...` under the hood.
195
201
 
196
202
  ```javascript
197
- import connection from "@seip/blue-bird/core/database.js";
203
+ import { Database, DB_TYPE } from "@seip/blue-bird/core/database.js";
204
+
205
+ const connection = new Database(20);
198
206
 
199
- // Basic SELECT query returning single row
207
+ // Basic SELECT query returning single row (Supports both MySQL and PostgreSQL)
200
208
  const user = await connection.query("SELECT * FROM users WHERE id = ?", [1], "return_row");
201
209
 
202
210
  // Query caching in Redis (stores results in Redis for 60 seconds)
203
211
  const stats = await connection.query("SELECT COUNT(*) as cnt FROM logs", [], { cache: 60 });
204
212
 
205
- // INSERT query returns insertId directly
213
+ // INSERT query returns insertId directly (or row ID/rowCount in Postgres)
206
214
  const newUserId = await connection.query("INSERT INTO users (name) VALUES (?)", ["Alice"]);
207
215
  ```
208
216
 
package/README.md CHANGED
@@ -39,7 +39,11 @@ npm install @seip/blue-bird
39
39
  npx blue-bird
40
40
  ```
41
41
 
42
- _This copies the base structure: `backend`, `frontend`, `docker`, `docker-compose.yml`, `AGENTS.md`, and `.env`._
42
+ When run, the interactive CLI prompts for your preferred infrastructure configuration:
43
+ - Database Selection: Choose between `none`, `mysql`, or `postgres`.
44
+ - Credentials: Set your database name, user, password, and port (`3306` or `5432`).
45
+
46
+ The CLI intelligently copies the appropriate Docker configuration (`docker/docker-compose.mysql.yml`, `docker/docker-compose.postgres.yml`, or `docker/docker-compose.none.yml`) to your project root as `docker-compose.yml`. It also writes the environment settings (`DB_TYPE`, `DATABASE_URL`) to `.env` and installs the required database packages (`mysql2` or `pg`) automatically.
43
47
 
44
48
  ### 3. Run Development Server / Modo Desarrollo
45
49
 
@@ -220,18 +224,38 @@ webRouter.use(App.helmet());
220
224
 
221
225
  ### 7. Database wrapper (`Database`)
222
226
 
223
- MySQL database client connection pool configuration featuring automated retry loops, query formatting utilities, and Redis query caching.
227
+ Blue Bird provides a unified, multi-database client wrapper (`core/database.js`) supporting **MySQL** and **PostgreSQL** with automated connection retry loops, query formatting utilities, and Redis query caching.
228
+
229
+ #### Driver Support
230
+ - **Native MySQL (`mysql2/promise`)**: High-performance connection pool for MySQL 8.0+.
231
+ - **Native PostgreSQL (`pg`)**: Connection pool for PostgreSQL 18+. When running standard queries with `connection.query(sql, params)`, the wrapper automatically converts `?` parameter placeholders into PostgreSQL `$1, $2, ...` syntax, allowing unified SQL query writing across both database engines.
232
+ - **No Database (`none`)**: If no database is configured, the wrapper is disabled gracefully without crashing the server.
233
+
234
+ #### Standalone & Remote Database Configuration
235
+ You can connect to any local or remote database instance (outside Docker, such as Supabase, Neon, AWS RDS, or local services) simply by defining the `DATABASE_URL` in your `.env` file:
236
+
237
+ ```env
238
+ DB_TYPE="postgres"
239
+ DATABASE_URL="postgresql://postgres:password@localhost:5432/blue_bird?schema=public"
240
+ # OR for MySQL:
241
+ # DATABASE_URL="mysql://root:password@localhost:3306/blue_bird"
242
+ ```
243
+
244
+ #### Usage Examples
224
245
 
225
246
  ```javascript
226
- import connection from "@seip/blue-bird/core/database.js";
247
+ import { Database, DB_TYPE } from "@seip/blue-bird/core/database.js";
248
+
249
+ // Instantiate the database connection pool with a connection limit (e.g., 20)
250
+ const connection = new Database(20);
227
251
 
228
- // Fetch single row from a SELECT query
252
+ // 1. Basic SELECT query returning single row (Works for both MySQL and PostgreSQL using ? placeholders)
229
253
  const user = await connection.query("SELECT * FROM users WHERE email = ?", ["test@example.com"], "return_row");
230
254
 
231
- // Fetch rows with 60 seconds Redis caching enabled
255
+ // 2. Fetch rows with 60 seconds Redis caching enabled
232
256
  const stats = await connection.query("SELECT COUNT(*) as count FROM access_logs", [], { cache: 60 });
233
257
 
234
- // INSERT queries return the last insert ID directly
258
+ // 3. INSERT query (returns insertId for MySQL, or inserted row ID / rowCount for PostgreSQL)
235
259
  const newId = await connection.query("INSERT INTO users (name) VALUES (?)", ["John"]);
236
260
  ```
237
261
 
@@ -260,9 +284,9 @@ Nginx reverse proxy is preconfigured with a page cache zone (`astro_cache`) that
260
284
 
261
285
  ---
262
286
 
263
- ## 🐳 Docker CLI Workflow
287
+ ## Docker CLI Workflow
264
288
 
265
- Blue Bird comes with a built-in Docker CLI wrapper that handles both local development database bootstrapping and full-stack VPS production deployments.
289
+ Blue Bird comes with a built-in Docker CLI wrapper that handles both local development database bootstrapping and full-stack VPS production deployments across MySQL, PostgreSQL, or no-database architectures.
266
290
 
267
291
  ### Commands Syntax:
268
292
 
@@ -272,16 +296,16 @@ npx blue-bird docker <command> [options]
272
296
 
273
297
  ### Supported Actions:
274
298
 
275
- - **`npx blue-bird docker start`**: Boots the production stack (Node.js App + Nginx + MySQL + Redis).
276
- - **`npx blue-bird docker start mysql`**: Boots the MySQL container only (great for local HTTP development).
299
+ - **`npx blue-bird docker start`**: Boots the production stack (Node.js App + Nginx + Database + Redis).
300
+ - **`npx blue-bird docker start db`** (or `postgres` / `mysql`): Boots the configured database container only (great for local development outside Docker).
277
301
  - **`npx blue-bird docker start redis`**: Boots the Redis container only.
278
- - **`npx blue-bird docker start dbs`**: Boots both database containers (MySQL + Redis).
279
- - **`npx blue-bird docker stop`**: Stops all active containers.
302
+ - **`npx blue-bird docker start dbs`**: Boots both database containers (configured DB + Redis).
303
+ - **`npx blue-bird docker stop`**: Stops all active project containers.
280
304
  - **`npx blue-bird docker build [--no-cache]`**: Builds or updates the Node.js production image.
281
305
  - **`npx blue-bird docker ps`**: Lists running project containers and ports.
282
- - **`npx blue-bird docker logs [app|mysql]`**: Tails logs for the specified container.
306
+ - **`npx blue-bird docker logs [app|db|postgres|mysql]`**: Tails logs for the specified container.
283
307
  - **`npx blue-bird docker pm2 [args]`**: Runs PM2 commands inside the Node.js application container (e.g. `status`, `monit`, `reload all`).
284
- - **`npx blue-bird docker db`**: Connects into the container's interactive MySQL shell using credentials from `.env`.
308
+ - **`npx blue-bird docker db`** (or `psql` / `mysql`): Connects into the container's interactive database shell (`psql` for PostgreSQL, `mysql` for MySQL) using credentials from `.env`.
285
309
  - **`npx blue-bird docker redis`**: Connects into the container's interactive Redis CLI terminal.
286
310
  - **`npx blue-bird docker prune`**: Safely clears orphaned volumes, dangling build caches, and images.
287
311
 
@@ -25,6 +25,32 @@ function getEnvVars() {
25
25
  return env;
26
26
  }
27
27
 
28
+ /**
29
+ * Determines the target database type (mysql or postgres) from environment variables.
30
+ * @param {Object} [env] - Environment dictionary.
31
+ * @returns {string} 'postgres' or 'mysql'.
32
+ */
33
+ function getDbType(env = getEnvVars()) {
34
+ if (env.DB_TYPE && (env.DB_TYPE.toLowerCase() === "postgres" || env.DB_TYPE.toLowerCase() === "postgresql" || env.DB_TYPE.toLowerCase() === "pg")) {
35
+ return "postgres";
36
+ }
37
+ if (env.DB_TYPE && env.DB_TYPE.toLowerCase() === "mysql") {
38
+ return "mysql";
39
+ }
40
+ if (env.DB_TYPE && env.DB_TYPE.toLowerCase() === "none") {
41
+ return "none";
42
+ }
43
+ if (env.DATABASE_URL && !env.DATABASE_URL.startsWith("#")) {
44
+ if (env.DATABASE_URL.startsWith("postgres://") || env.DATABASE_URL.startsWith("postgresql://")) {
45
+ return "postgres";
46
+ }
47
+ if (env.DATABASE_URL.startsWith("mysql://")) {
48
+ return "mysql";
49
+ }
50
+ }
51
+ return "mysql";
52
+ }
53
+
28
54
  /**
29
55
  * Spawns a child process and inherits standard IO for interactive terminal sessions.
30
56
  * @param {string} command - The binary to execute.
@@ -56,14 +82,20 @@ function checkComposeFile() {
56
82
  */
57
83
  async function startCommand(service) {
58
84
  checkComposeFile();
85
+ const dbType = getDbType();
59
86
 
60
- if (service === "mysql" || service === "--mysql" || service === "dev") {
61
- console.log(chalk.cyan("Starting MySQL container..."));
62
- const code = await runCmd("docker", ["compose", "up", "-d", "mysql"]);
87
+ if (service === "mysql" || service === "--mysql" || service === "postgres" || service === "--postgres" || service === "db" || service === "--db" || service === "dev") {
88
+ const targetContainer = (service === "postgres" || service === "--postgres") ? "postgres" : ((service === "mysql" || service === "--mysql") ? "mysql" : dbType);
89
+ if (targetContainer === "none") {
90
+ console.log(chalk.yellow("[INFO] DB_TYPE is set to 'none', skipping database container startup."));
91
+ return;
92
+ }
93
+ console.log(chalk.cyan(`Starting ${targetContainer.toUpperCase()} container...`));
94
+ const code = await runCmd("docker", ["compose", "up", "-d", targetContainer]);
63
95
  if (code === 0) {
64
- console.log(chalk.green("MySQL started."));
96
+ console.log(chalk.green(`${targetContainer.toUpperCase()} started.`));
65
97
  } else {
66
- console.error(chalk.red("Error starting MySQL."));
98
+ console.error(chalk.red(`Error starting ${targetContainer}. Make sure '${targetContainer}' service is defined in docker-compose.yml.`));
67
99
  process.exit(1);
68
100
  }
69
101
  } else if (service === "redis" || service === "--redis") {
@@ -76,8 +108,19 @@ async function startCommand(service) {
76
108
  process.exit(1);
77
109
  }
78
110
  } else if (service === "dbs" || service === "databases") {
79
- console.log(chalk.cyan("Starting Database containers (MySQL + Redis)..."));
80
- const code = await runCmd("docker", ["compose", "up", "-d", "mysql", "redis"]);
111
+ if (dbType === "none") {
112
+ console.log(chalk.cyan("DB_TYPE is 'none', starting Redis container only..."));
113
+ const code = await runCmd("docker", ["compose", "up", "-d", "redis"]);
114
+ if (code === 0) {
115
+ console.log(chalk.green("Redis started."));
116
+ } else {
117
+ console.error(chalk.red("Error starting Redis."));
118
+ process.exit(1);
119
+ }
120
+ return;
121
+ }
122
+ console.log(chalk.cyan(`Starting Database containers (${dbType.toUpperCase()} + Redis)...`));
123
+ const code = await runCmd("docker", ["compose", "up", "-d", dbType, "redis"]);
81
124
  if (code === 0) {
82
125
  console.log(chalk.green("Database containers started."));
83
126
  } else {
@@ -85,7 +128,7 @@ async function startCommand(service) {
85
128
  process.exit(1);
86
129
  }
87
130
  } else if (service === "prod" || service === "app" || service === "--app" || !service) {
88
- console.log(chalk.cyan("Starting production stack (MySQL + Redis + App + Nginx)..."));
131
+ console.log(chalk.cyan("Starting production stack (DB + Redis + App + Nginx)..."));
89
132
  const code = await runCmd("docker", ["compose", "--profile", "prod", "up", "-d"]);
90
133
  if (code === 0) {
91
134
  console.log(chalk.green("Production stack started."));
@@ -94,7 +137,7 @@ async function startCommand(service) {
94
137
  process.exit(1);
95
138
  }
96
139
  } else {
97
- console.error(chalk.red(`Unknown service '${service}'. Use: mysql, redis, dbs, prod.`));
140
+ console.error(chalk.red(`Unknown service '${service}'. Use: mysql, postgres, db, redis, dbs, prod.`));
98
141
  process.exit(1);
99
142
  }
100
143
  }
@@ -105,16 +148,22 @@ async function startCommand(service) {
105
148
  */
106
149
  async function stopCommand(service) {
107
150
  checkComposeFile();
151
+ const dbType = getDbType();
108
152
 
109
153
  if (!service || service === "all") {
110
154
  console.log(chalk.cyan("Stopping all Blue Bird containers..."));
111
155
  await runCmd("docker", ["compose", "--profile", "prod", "down"]);
112
156
  console.log(chalk.green("All containers stopped."));
113
- } else if (service === "mysql" || service === "--mysql") {
114
- console.log(chalk.cyan("Stopping MySQL..."));
115
- await runCmd("docker", ["compose", "stop", "mysql"]);
116
- await runCmd("docker", ["compose", "rm", "-f", "mysql"]);
117
- console.log(chalk.green("MySQL stopped."));
157
+ } else if (service === "mysql" || service === "--mysql" || service === "postgres" || service === "--postgres" || service === "db" || service === "--db") {
158
+ const targetContainer = (service === "postgres" || service === "--postgres") ? "postgres" : ((service === "mysql" || service === "--mysql") ? "mysql" : dbType);
159
+ if (targetContainer === "none") {
160
+ console.log(chalk.yellow("[INFO] DB_TYPE is set to 'none', no database container to stop."));
161
+ return;
162
+ }
163
+ console.log(chalk.cyan(`Stopping ${targetContainer.toUpperCase()}...`));
164
+ await runCmd("docker", ["compose", "stop", targetContainer]);
165
+ await runCmd("docker", ["compose", "rm", "-f", targetContainer]);
166
+ console.log(chalk.green(`${targetContainer.toUpperCase()} stopped.`));
118
167
  } else if (service === "redis" || service === "--redis") {
119
168
  console.log(chalk.cyan("Stopping Redis..."));
120
169
  await runCmd("docker", ["compose", "stop", "redis"]);
@@ -126,7 +175,7 @@ async function stopCommand(service) {
126
175
  await runCmd("docker", ["compose", "--profile", "prod", "rm", "-f", "app"]);
127
176
  console.log(chalk.green("App container stopped."));
128
177
  } else {
129
- console.error(chalk.red(`Unknown service '${service}'. Use: all, mysql, redis, app.`));
178
+ console.error(chalk.red(`Unknown service '${service}'. Use: all, mysql, postgres, db, redis, app.`));
130
179
  process.exit(1);
131
180
  }
132
181
  }
@@ -170,7 +219,15 @@ async function psCommand() {
170
219
  async function logsCommand(service, followOpt) {
171
220
  checkComposeFile();
172
221
  const follow = followOpt !== "--no-follow";
173
- const targetService = service === "mysql" ? "mysql" : "app";
222
+ const dbType = getDbType();
223
+ let targetService = "app";
224
+ if (service === "mysql" || service === "postgres" || service === "db") {
225
+ targetService = service === "db" ? dbType : service;
226
+ }
227
+ if (targetService === "none") {
228
+ console.log(chalk.yellow("[INFO] DB_TYPE is set to 'none', no database container logs to display."));
229
+ return;
230
+ }
174
231
 
175
232
  const cmdArgs = ["compose"];
176
233
  if (targetService === "app") {
@@ -186,44 +243,72 @@ async function logsCommand(service, followOpt) {
186
243
  }
187
244
 
188
245
  /**
189
- * Handles interactive shell connections into the MySQL container.
246
+ * Handles interactive shell connections into the MySQL or PostgreSQL container.
190
247
  * @param {string} userOpt - DB username.
191
248
  * @param {string} passOpt - DB password.
192
249
  * @param {string} dbOpt - DB database name.
193
- * @param {boolean} rootOpt - Flag for overriding database credentials to connect as root.
250
+ * @param {boolean} rootOpt - Flag for overriding database credentials to connect as root/postgres.
251
+ * @param {string} explicitService - Explicit target service if specified ('mysql' or 'postgres').
194
252
  */
195
- async function mysqlCommand(userOpt, passOpt, dbOpt, rootOpt) {
253
+ async function dbClientCommand(userOpt, passOpt, dbOpt, rootOpt, explicitService) {
196
254
  checkComposeFile();
197
255
  const env = getEnvVars();
256
+ const dbType = explicitService === "postgres" || explicitService === "psql" ? "postgres" : (explicitService === "mysql" ? "mysql" : getDbType(env));
257
+ if (dbType === "none") {
258
+ console.error(chalk.yellow("[INFO] DB_TYPE is set to 'none'. No database container available to connect to."));
259
+ return;
260
+ }
198
261
 
199
262
  let dbUser = userOpt;
200
263
  let dbPass = passOpt;
201
264
  let dbName = dbOpt;
202
265
 
203
- if (rootOpt) {
204
- dbUser = "root";
205
- dbPass = env.DB_PASSWORD || "root";
266
+ if (dbType === "postgres") {
267
+ if (rootOpt) {
268
+ dbUser = "postgres";
269
+ } else {
270
+ dbUser = dbUser || env.DB_USER || "postgres";
271
+ }
272
+ dbName = dbName || env.DB_NAME || "blue_bird";
273
+
274
+ const targetDb = dbName ? ` (database: ${dbName})` : "";
275
+ console.log(chalk.cyan(`Connecting to PostgreSQL shell (psql) in container as '${dbUser}'${targetDb}...`));
276
+
277
+ const cmdArgs = ["compose", "exec", "postgres", "psql", `-U${dbUser}`];
278
+ if (dbName) {
279
+ cmdArgs.push("-d", dbName);
280
+ }
281
+ const code = await runCmd("docker", cmdArgs);
282
+ if (code !== 0) {
283
+ console.error(chalk.yellow("Make sure the PostgreSQL container is running: npx blue-bird docker start postgres"));
284
+ process.exit(1);
285
+ }
206
286
  } else {
207
- dbUser = dbUser || env.DB_USER || "root";
208
- dbPass = dbPass || env.DB_PASSWORD || "root";
209
- }
210
- dbName = dbName || env.DB_NAME || "blue_bird";
287
+ if (rootOpt) {
288
+ dbUser = "root";
289
+ dbPass = env.DB_PASSWORD || "root";
290
+ } else {
291
+ dbUser = dbUser || env.DB_USER || "root";
292
+ dbPass = dbPass || env.DB_PASSWORD || "root";
293
+ }
294
+ dbName = dbName || env.DB_NAME || "blue_bird";
211
295
 
212
- const cmdArgs = ["compose", "exec", "mysql", "mysql", `-u${dbUser}`];
213
- if (dbPass) {
214
- cmdArgs.push(`-p${dbPass}`);
215
- }
216
- if (dbName) {
217
- cmdArgs.push(dbName);
218
- }
296
+ const cmdArgs = ["compose", "exec", "mysql", "mysql", `-u${dbUser}`];
297
+ if (dbPass) {
298
+ cmdArgs.push(`-p${dbPass}`);
299
+ }
300
+ if (dbName) {
301
+ cmdArgs.push(dbName);
302
+ }
219
303
 
220
- const targetDb = dbName ? ` (database: {dbName})` : "";
221
- console.log(chalk.cyan(`Connecting to MySQL shell in container as '${dbUser}'${targetDb}...`));
304
+ const targetDb = dbName ? ` (database: ${dbName})` : "";
305
+ console.log(chalk.cyan(`Connecting to MySQL shell in container as '${dbUser}'${targetDb}...`));
222
306
 
223
- const code = await runCmd("docker", cmdArgs);
224
- if (code !== 0) {
225
- console.error(chalk.yellow("Make sure the MySQL container is running: npx blue-bird docker start mysql"));
226
- process.exit(1);
307
+ const code = await runCmd("docker", cmdArgs);
308
+ if (code !== 0) {
309
+ console.error(chalk.yellow("Make sure the MySQL container is running: npx blue-bird docker start mysql"));
310
+ process.exit(1);
311
+ }
227
312
  }
228
313
  }
229
314
 
@@ -338,6 +423,8 @@ async function main() {
338
423
  await redisCommand();
339
424
  break;
340
425
  case "mysql":
426
+ case "postgres":
427
+ case "psql":
341
428
  case "db": {
342
429
  let user, password, db, root = false;
343
430
  for (let i = 1; i < args.length; i++) {
@@ -346,7 +433,7 @@ async function main() {
346
433
  else if (args[i] === "-d" || args[i] === "--db") db = args[++i];
347
434
  else if (args[i] === "--root") root = true;
348
435
  }
349
- await mysqlCommand(user, password, db, root);
436
+ await dbClientCommand(user, password, db, root, command);
350
437
  break;
351
438
  }
352
439
  case "df":
@@ -363,7 +450,7 @@ async function main() {
363
450
  }
364
451
  default:
365
452
  console.log(chalk.yellow(`Unknown docker command: ${command}`));
366
- console.log("Available commands: start, stop, build, ps, logs, pm2, mysql/db, redis, df/disk, prune/clean");
453
+ console.log("Available commands: start, stop, build, ps, logs, pm2, mysql/postgres/db, redis, df/disk, prune/clean");
367
454
  }
368
455
  }
369
456