@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 +4 -6
- package/AGENTS.md +20 -12
- package/README.md +38 -14
- package/core/cli/docker.js +128 -41
- package/core/cli/init.js +296 -198
- package/core/database.js +138 -57
- package/docker/docker-compose.mysql.yml +92 -0
- package/docker/docker-compose.none.yml +68 -0
- package/docker/docker-compose.postgres.yml +93 -0
- package/docker-compose.yml +1 -2
- package/frontend/src/http/api.js +14 -9
- package/package.json +3 -4
- package/backend/logs/2026-07-14/info.log +0 -48
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
|
-
#
|
|
25
|
-
DATABASE_URL="
|
|
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://
|
|
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:
|
|
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 (
|
|
170
|
-
npx blue-bird docker start
|
|
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 (
|
|
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
|
|
179
|
-
npx blue-bird docker
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
-
##
|
|
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 +
|
|
276
|
-
- **`npx blue-bird docker start mysql
|
|
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 (
|
|
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
|
|
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
|
|
package/core/cli/docker.js
CHANGED
|
@@ -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
|
-
|
|
62
|
-
|
|
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(
|
|
96
|
+
console.log(chalk.green(`${targetContainer.toUpperCase()} started.`));
|
|
65
97
|
} else {
|
|
66
|
-
console.error(chalk.red(
|
|
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
|
-
|
|
80
|
-
|
|
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 (
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
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
|
|
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 (
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
221
|
-
|
|
304
|
+
const targetDb = dbName ? ` (database: ${dbName})` : "";
|
|
305
|
+
console.log(chalk.cyan(`Connecting to MySQL shell in container as '${dbUser}'${targetDb}...`));
|
|
222
306
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
|
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
|
|