@seip/blue-bird 1.1.0 → 1.1.1
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 +25 -12
- package/AGENTS.md +97 -25
- package/README.md +80 -23
- package/core/app.js +42 -0
- package/core/cache.js +62 -30
- package/core/cli/docker.js +219 -42
- package/core/cli/init.js +92 -8
- package/core/database.js +205 -20
- package/core/hash.js +201 -0
- package/core/index.d.ts +57 -0
- package/core/upload.js +83 -57
- package/core/ws.js +18 -1
- package/docker/docker-compose.sqlite.yml +69 -0
- package/package.json +6 -3
package/.env_example
CHANGED
|
@@ -13,7 +13,11 @@ DESCRIPTION="Description project"
|
|
|
13
13
|
# Security Configuration
|
|
14
14
|
JWT_SECRET="JWT_SECRET"
|
|
15
15
|
|
|
16
|
-
#
|
|
16
|
+
# Cache Configuration
|
|
17
|
+
# Options: "memory" (Fast in-memory RAM), "redis" (Distributed container cache), "none" (Disabled)
|
|
18
|
+
CACHE_MODE="memory"
|
|
19
|
+
|
|
20
|
+
# Redis Configuration (Used when CACHE_MODE="redis")
|
|
17
21
|
REDIS_HOST="localhost"
|
|
18
22
|
REDIS_PORT=6379
|
|
19
23
|
REDIS_PASSWORD=""
|
|
@@ -21,16 +25,25 @@ REDIS_PASSWORD=""
|
|
|
21
25
|
# PM2 Clustering Instances (integer or 'max')
|
|
22
26
|
PM2_INSTANCES=1
|
|
23
27
|
|
|
24
|
-
# Database Configuration (Used only for local development outside Docker)
|
|
25
|
-
# MySQL
|
|
26
|
-
DATABASE_URL="mysql://root:root@localhost:3306/blue_bird"
|
|
27
28
|
|
|
28
|
-
#
|
|
29
|
-
#
|
|
29
|
+
# Database Configuration
|
|
30
|
+
# SQLite (Default - uses WAL mode and busy timeout automatically)
|
|
31
|
+
DB_TYPE="sqlite"
|
|
32
|
+
DB_FILE="database/blue_bird.db"
|
|
33
|
+
DATABASE_URL="sqlite:database/blue_bird.db"
|
|
34
|
+
|
|
35
|
+
# MySQL (Uncomment to use MySQL in Docker or locally)
|
|
36
|
+
# DB_TYPE="mysql"
|
|
37
|
+
# DB_NAME="blue_bird"
|
|
38
|
+
# DB_USER="root"
|
|
39
|
+
# DB_PASSWORD="root"
|
|
40
|
+
# DB_PORT=3306
|
|
41
|
+
# DATABASE_URL="mysql://root:root@localhost:3306/blue_bird"
|
|
30
42
|
|
|
31
|
-
#
|
|
32
|
-
DB_TYPE="
|
|
33
|
-
DB_NAME="blue_bird"
|
|
34
|
-
DB_USER="
|
|
35
|
-
DB_PASSWORD="root"
|
|
36
|
-
DB_PORT=
|
|
43
|
+
# PostgreSQL (Uncomment to use PostgreSQL in Docker or locally)
|
|
44
|
+
# DB_TYPE="postgres"
|
|
45
|
+
# DB_NAME="blue_bird"
|
|
46
|
+
# DB_USER="postgres"
|
|
47
|
+
# DB_PASSWORD="root"
|
|
48
|
+
# DB_PORT=5432
|
|
49
|
+
# DATABASE_URL="postgresql://postgres:root@localhost:5432/blue_bird?schema=public"
|
package/AGENTS.md
CHANGED
|
@@ -47,11 +47,30 @@ routerApi.post("/users", validateUser.middleware(), (req, res) => {
|
|
|
47
47
|
});
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
## 5. Authentication (Auth)
|
|
50
|
+
## 5. Authentication & Password Hashing (Auth & Hash)
|
|
51
|
+
|
|
52
|
+
### Password Hashing (Hash)
|
|
53
|
+
|
|
54
|
+
Blue Bird includes native password hashing using `node:crypto.scrypt` with random salt and timing-safe comparison (zero external npm dependencies required). It also seamlessly supports `bcrypt` when installed or verifying `$2a$/$2b$` hashes.
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
import Hash from "@seip/blue-bird/core/hash.js";
|
|
58
|
+
|
|
59
|
+
// Hash password with scrypt (default)
|
|
60
|
+
const hash = await Hash.make("mySecretPassword");
|
|
61
|
+
|
|
62
|
+
// Verify password
|
|
63
|
+
const isValid = await Hash.verify("mySecretPassword", hash);
|
|
64
|
+
|
|
65
|
+
// Hash with bcrypt (if 'bcrypt' package is installed)
|
|
66
|
+
const bcryptHash = await Hash.make("mySecretPassword", { driver: "bcrypt" });
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### JWT Handling (Auth)
|
|
51
70
|
|
|
52
71
|
The system includes built-in JWT handling with AES-256-GCM encryption. The framework handles tokens via Cookies or the `Authorization` header.
|
|
53
72
|
|
|
54
|
-
|
|
73
|
+
#### Protecting Routes
|
|
55
74
|
|
|
56
75
|
Use `Auth.protect()` as a middleware to secure routes.
|
|
57
76
|
|
|
@@ -63,7 +82,7 @@ router.get("/profile", Auth.protect(), (req, res) => {
|
|
|
63
82
|
});
|
|
64
83
|
```
|
|
65
84
|
|
|
66
|
-
|
|
85
|
+
#### Login and Logout
|
|
67
86
|
|
|
68
87
|
The `Auth` class provides helpers to handle session management via cookies.
|
|
69
88
|
|
|
@@ -71,25 +90,50 @@ The `Auth` class provides helpers to handle session management via cookies.
|
|
|
71
90
|
router.post("/login", async (req, res) => {
|
|
72
91
|
const user = { id: 1, name: "John" };
|
|
73
92
|
await Auth.login(res, user);
|
|
74
|
-
res.
|
|
93
|
+
res.ok(user, "Logged in");
|
|
75
94
|
});
|
|
76
95
|
|
|
77
96
|
router.post("/logout", async (req, res) => {
|
|
78
97
|
await Auth.logout(res);
|
|
79
|
-
res.
|
|
98
|
+
res.ok(null, "Logged out");
|
|
80
99
|
});
|
|
81
100
|
```
|
|
82
101
|
|
|
83
|
-
## 6.
|
|
102
|
+
## 6. HTTP Response Helpers & Health Check
|
|
84
103
|
|
|
85
|
-
|
|
104
|
+
Blue Bird decorates the Express `res` object with standardized helper methods:
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
// Success responses
|
|
108
|
+
res.ok(data, "Success message"); // HTTP 200 { status: "success", message, data }
|
|
109
|
+
res.created(data, "Created successfully"); // HTTP 201 { status: "success", message, data }
|
|
110
|
+
res.paginate(items, pagination, "Fetched"); // HTTP 200 { status: "success", message, data, pagination }
|
|
111
|
+
|
|
112
|
+
// Error responses
|
|
113
|
+
res.badRequest("Invalid input", errors); // HTTP 400 { status: "error", message, errors }
|
|
114
|
+
res.unauthorized("Authentication required");// HTTP 401 { status: "error", message }
|
|
115
|
+
res.forbidden("Access denied"); // HTTP 403 { status: "error", message }
|
|
116
|
+
res.notFound("Resource not found"); // HTTP 404 { status: "error", message }
|
|
117
|
+
res.serverError("Internal failure", err); // HTTP 500 { status: "error", message }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Built-in Health Endpoint
|
|
121
|
+
|
|
122
|
+
Every Blue Bird application provides a native `/api/health` endpoint out of the box returning system uptime, timestamp, environment, and memory consumption.
|
|
123
|
+
|
|
124
|
+
## 7. Performance Caching (Cache)
|
|
125
|
+
|
|
126
|
+
Configure caching via `CACHE_MODE` in `.env`:
|
|
127
|
+
- `CACHE_MODE="memory"`: Fast local RAM cache inside Node.js (default when Redis is not used). Zero network overhead.
|
|
128
|
+
- `CACHE_MODE="redis"`: Distributed cache across containers with automatic fallback to memory if Redis is unavailable.
|
|
129
|
+
- `CACHE_MODE="none"`: Caching disabled.
|
|
86
130
|
|
|
87
131
|
```javascript
|
|
88
132
|
import Cache from "@seip/blue-bird/core/cache.js";
|
|
89
133
|
|
|
90
134
|
// Express route middleware caching
|
|
91
135
|
router.get("/stats", Cache.middleware(60), (req, res) => {
|
|
92
|
-
res.
|
|
136
|
+
res.ok({ usersCount: 150 });
|
|
93
137
|
});
|
|
94
138
|
|
|
95
139
|
// Programmatic cache manipulation
|
|
@@ -100,9 +144,22 @@ const cachedData = await Cache.get("custom_key");
|
|
|
100
144
|
await Cache.delete("/api/public/config");
|
|
101
145
|
```
|
|
102
146
|
|
|
103
|
-
|
|
147
|
+
## 8. On-Demand Modules & CLI Add
|
|
148
|
+
|
|
149
|
+
To keep `node_modules` lightweight, optional modules load dynamically on-demand. Install them easily via:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npx blue-bird add upload # Installs multer for file uploads
|
|
153
|
+
npx blue-bird add ws # Installs ws for WebSockets
|
|
154
|
+
npx blue-bird add redis # Installs redis for caching/sessions
|
|
155
|
+
npx blue-bird add sqlite # Installs better-sqlite3
|
|
156
|
+
npx blue-bird add mysql # Installs mysql2
|
|
157
|
+
npx blue-bird add postgres # Installs pg
|
|
158
|
+
npx blue-bird add bcrypt # Installs bcrypt
|
|
159
|
+
npx blue-bird add swagger # Installs swagger-ui-express
|
|
160
|
+
```
|
|
104
161
|
|
|
105
|
-
##
|
|
162
|
+
## 9. Security (Helmet)
|
|
106
163
|
|
|
107
164
|
Helmet is **not applied globally** by default. Apply it per-router where needed:
|
|
108
165
|
|
|
@@ -113,65 +170,80 @@ const apiRouter = new Router("/api");
|
|
|
113
170
|
apiRouter.use(App.helmet());
|
|
114
171
|
```
|
|
115
172
|
|
|
116
|
-
## 8. Docker Compose CLI
|
|
117
173
|
|
|
118
|
-
|
|
174
|
+
## 10. Docker Compose CLI
|
|
175
|
+
|
|
176
|
+
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 SQLite (default), MySQL, PostgreSQL, or no-database (`none`) architectures.
|
|
119
177
|
|
|
120
178
|
Production deployments always use Docker for orchestration, running:
|
|
121
179
|
- Nginx: Serves static files directly from `frontend/` (stripping `.html` extensions) and blocks common scanner requests (`.env`, `.git`, etc.) with fallback to Express for APIs.
|
|
122
180
|
- Node.js App: Managed via PM2 in cluster mode using `PM2_INSTANCES` configuration (defaults to `1`, can be set to `max`).
|
|
123
|
-
- Database: MySQL (`mysql:8.0`) or PostgreSQL (`postgres:18-alpine`), dynamically detected via `getDbType()` reading `DB_TYPE` / `DATABASE_URL` from `.env`.
|
|
181
|
+
- Database: SQLite (default, embedded in app with volume `./database`), MySQL (`mysql:8.0`), or PostgreSQL (`postgres:18-alpine`), dynamically detected via `getDbType()` reading `DB_TYPE` / `DATABASE_URL` from `.env`.
|
|
124
182
|
- Redis: Memory caching and session store.
|
|
125
183
|
|
|
126
184
|
```bash
|
|
127
185
|
# Manage containers using blue-bird CLI
|
|
128
186
|
npx blue-bird docker dev # Starts development database & redis containers. Run npm run dev manually.
|
|
129
|
-
npx blue-bird docker start # Starts production app stack (DB, redis, app, nginx)
|
|
130
|
-
npx blue-bird docker start db # Starts configured database container
|
|
187
|
+
npx blue-bird docker start # Starts production app stack (DB/SQLite, redis, app, nginx)
|
|
188
|
+
npx blue-bird docker start db # Starts configured database container (or Redis if SQLite is used)
|
|
131
189
|
npx blue-bird docker start redis # Starts Redis container only
|
|
132
|
-
npx blue-bird docker start dbs # Starts
|
|
190
|
+
npx blue-bird docker start dbs # Starts database containers (configured DB + Redis)
|
|
133
191
|
npx blue-bird docker stop # Stops all running containers
|
|
134
192
|
npx blue-bird docker build # Builds/rebuilds application image
|
|
135
193
|
npx blue-bird docker ps # Shows status of active containers
|
|
136
194
|
npx blue-bird docker logs # Tails Node.js app container logs
|
|
137
|
-
npx blue-bird docker logs db # Tails
|
|
195
|
+
npx blue-bird docker logs db # Tails database container logs (or app logs if SQLite is used)
|
|
138
196
|
npx blue-bird docker pm2 [args] # Runs PM2 commands inside the app container (e.g. status, monit)
|
|
139
|
-
npx blue-bird docker db #
|
|
197
|
+
npx blue-bird docker db # Inspects active database (SQLite query/tables, psql for Postgres, mysql for MySQL)
|
|
198
|
+
npx blue-bird docker sqlite # Inspects SQLite tables or runs query on configured .db file
|
|
140
199
|
npx blue-bird docker psql # Runs interactive PostgreSQL client terminal inside container
|
|
141
200
|
npx blue-bird docker mysql # Runs interactive MySQL client terminal inside container
|
|
142
201
|
npx blue-bird docker redis # Runs interactive Redis client terminal inside container
|
|
202
|
+
npx blue-bird docker export # Exports database backup (.db file for SQLite or .sql dump) into backups/
|
|
203
|
+
npx blue-bird docker import # Restores database backup (.db or .sql) from backups/
|
|
143
204
|
npx blue-bird docker prune # Cleans unused volumes, dangling images, and BuildKit caches
|
|
144
205
|
```
|
|
145
206
|
|
|
146
207
|
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`.
|
|
147
208
|
|
|
148
|
-
##
|
|
209
|
+
## 11. AI Development Guidelines
|
|
149
210
|
|
|
150
211
|
1. **Frontend**: Static files are stored in `frontend/` (e.g. `frontend/css`, `frontend/js`). HTML files will be served without the `.html` extension (e.g. `login.html` is accessible as `/login`).
|
|
151
212
|
2. **JSON Responses**: API endpoints should return standardized responses formatted as `{ message: "..." }` or `{ data: ... }`.
|
|
152
213
|
3. **Magic Imports**: Stick to pure relative imports or well-configured aliases (imports natively resolve from `@seip/blue-bird/...` or relative directories like `../../`).
|
|
153
214
|
4. **No inline comments**: Only use JSDoc for documentation.
|
|
154
215
|
|
|
155
|
-
##
|
|
216
|
+
## 12. Database Module (database.js)
|
|
217
|
+
|
|
218
|
+
Blue Bird provides a unified wrapper class (`core/database.js`) supporting **SQLite (`better-sqlite3`)**, **MySQL (`mysql2/promise`)**, and **PostgreSQL (`pg`)**. It features connection pooling/reconnection, automatic retries on startup, query formatting, and built-in Redis query caching:
|
|
156
219
|
|
|
157
|
-
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:
|
|
158
220
|
|
|
159
|
-
- **Dynamic Initialization:** When `npx blue-bird` (`core/cli/init.js`) runs, it prompts the developer for the database type (`
|
|
160
|
-
- **
|
|
221
|
+
- **Dynamic Initialization:** When `npx blue-bird` (`core/cli/init.js`) runs, it prompts the developer for the database type (`sqlite` [default], `mysql`, `postgres`, `none`). It then intelligently copies the correct `docker-compose.yml` template (`docker-compose.sqlite.yml`, `docker-compose.mysql.yml`, `docker-compose.postgres.yml`, or `docker-compose.none.yml`) and configures `.env` with `DB_TYPE`, `DB_FILE`, and `DATABASE_URL`.
|
|
222
|
+
- **SQLite Concurrency & WAL:** SQLite automatically runs with `PRAGMA journal_mode = WAL;`, `PRAGMA busy_timeout = 5000;`, `PRAGMA synchronous = NORMAL;`, and `PRAGMA foreign_keys = ON;` to eliminate "database is locked" errors and ensure high concurrency with readers and writers.
|
|
223
|
+
- **Parameter Placeholders:** Supports `?` placeholders across all drivers (automatically translated to `$1, $2, ...` under the hood for PostgreSQL).
|
|
161
224
|
|
|
162
225
|
```javascript
|
|
163
226
|
import { Database, DB_TYPE } from "@seip/blue-bird/core/database.js";
|
|
164
227
|
|
|
165
228
|
const connection = new Database(20);
|
|
166
229
|
|
|
167
|
-
// Basic SELECT query returning single row (Supports
|
|
230
|
+
// Basic SELECT query returning single row (Supports SQLite, MySQL, and PostgreSQL)
|
|
168
231
|
const user = await connection.query("SELECT * FROM users WHERE id = ?", [1], "return_row");
|
|
169
232
|
|
|
170
233
|
// Query caching in Redis (stores results in Redis for 60 seconds)
|
|
171
234
|
const stats = await connection.query("SELECT COUNT(*) as cnt FROM logs", [], { cache: 60 });
|
|
172
235
|
|
|
173
|
-
// INSERT query returns insertId directly (or row ID
|
|
236
|
+
// INSERT query returns insertId directly (or row ID in SQLite / Postgres)
|
|
174
237
|
const newUserId = await connection.query("INSERT INTO users (name) VALUES (?)", ["Alice"]);
|
|
238
|
+
|
|
239
|
+
// Pagination helper
|
|
240
|
+
const page = await connection.paginate("SELECT * FROM users ORDER BY id ASC", [], { page: 1, limit: 10 });
|
|
241
|
+
|
|
242
|
+
// Safe Transactions
|
|
243
|
+
await connection.transaction(async (tx) => {
|
|
244
|
+
const id = await tx.query("INSERT INTO users (name) VALUES (?)", ["Bob"]);
|
|
245
|
+
await tx.query("INSERT INTO profiles (user_id) VALUES (?)", [id]);
|
|
246
|
+
});
|
|
175
247
|
```
|
|
176
248
|
|
|
177
249
|
## 11. Nginx Static Asset Caching
|
package/README.md
CHANGED
|
@@ -59,10 +59,11 @@ npx blue-bird
|
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
When run, the interactive CLI prompts for your preferred infrastructure configuration:
|
|
62
|
-
- Database Selection: Choose between `
|
|
63
|
-
- Credentials: Set your database
|
|
62
|
+
- Database Selection: Choose between `sqlite` (default), `mysql`, `postgres`, or `none`.
|
|
63
|
+
- Credentials / Path: Set your SQLite database path (default `database/blue_bird.db`), or MySQL/PostgreSQL host, port, user, and password.
|
|
64
|
+
|
|
65
|
+
The CLI intelligently copies the appropriate Docker configuration (`docker/docker-compose.sqlite.yml`, `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`, `DB_FILE`, `DATABASE_URL`) to `.env` and installs the required database packages (`better-sqlite3`, `mysql2`, or `pg`) automatically.
|
|
64
66
|
|
|
65
|
-
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.
|
|
66
67
|
|
|
67
68
|
### 3. Run Development Server / Modo Desarrollo
|
|
68
69
|
|
|
@@ -162,18 +163,37 @@ routerApi.post("/users", validateUser.middleware(), (req, res) => {
|
|
|
162
163
|
|
|
163
164
|
---
|
|
164
165
|
|
|
165
|
-
### 4.
|
|
166
|
+
### 4. Authentication & Password Hashing (`Auth` & `Hash`)
|
|
167
|
+
|
|
168
|
+
#### Password Hashing (`Hash`)
|
|
169
|
+
|
|
170
|
+
Blue Bird includes high-performance password hashing using `node:crypto.scrypt` with random salt and timing-safe comparison out of the box (zero npm dependencies). It also supports `bcrypt` if installed or when verifying `$2a$/$2b$` hashes.
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
import Hash from "@seip/blue-bird/core/hash.js";
|
|
174
|
+
|
|
175
|
+
// 1. Hash password with scrypt (default)
|
|
176
|
+
const hash = await Hash.make("mySecretPassword");
|
|
177
|
+
|
|
178
|
+
// 2. Verify password (timing-safe comparison)
|
|
179
|
+
const isValid = await Hash.verify("mySecretPassword", hash);
|
|
180
|
+
|
|
181
|
+
// 3. Hash with bcrypt (if 'bcrypt' package is installed)
|
|
182
|
+
const bcryptHash = await Hash.make("mySecretPassword", { driver: "bcrypt", rounds: 10 });
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### JWT Authentication & Sessions (`Auth`)
|
|
166
186
|
|
|
167
187
|
Secure user authentication with AES-256-GCM encrypted tokens. Transmitted via HTTP-Only cookies or `Authorization` headers, with optional Redis session storage and invalidation.
|
|
168
188
|
|
|
169
|
-
|
|
189
|
+
##### Protecting Routes
|
|
170
190
|
|
|
171
191
|
```javascript
|
|
172
192
|
import Auth from "@seip/blue-bird/core/auth.js";
|
|
173
193
|
|
|
174
194
|
// 1. Secure API endpoint (returns 401 JSON on failure)
|
|
175
195
|
router.get("/profile", Auth.protect(), (req, res) => {
|
|
176
|
-
res.
|
|
196
|
+
res.ok({ user: req.user });
|
|
177
197
|
});
|
|
178
198
|
|
|
179
199
|
// 2. Secure web page (redirects to /login on failure)
|
|
@@ -182,20 +202,20 @@ router.get("/dashboard", Auth.protect({ redirect: "/login", key: "user", cookieK
|
|
|
182
202
|
});
|
|
183
203
|
```
|
|
184
204
|
|
|
185
|
-
|
|
205
|
+
##### Authentication Sessions & Utilities
|
|
186
206
|
|
|
187
207
|
```javascript
|
|
188
208
|
// Login & Sync Session state in Redis (if active)
|
|
189
209
|
router.post("/login", async (req, res) => {
|
|
190
210
|
const user = { id: 1, name: "John Doe", role: "admin" };
|
|
191
211
|
await Auth.login(res, user, "auth", { expiresIn: "7d" });
|
|
192
|
-
res.
|
|
212
|
+
res.ok(user, "Logged in successfully");
|
|
193
213
|
});
|
|
194
214
|
|
|
195
215
|
// Logout & Delete Session from Redis
|
|
196
216
|
router.post("/logout", async (req, res) => {
|
|
197
217
|
await Auth.logout(res, "auth", {}, req);
|
|
198
|
-
res.
|
|
218
|
+
res.ok(null, "Logged out");
|
|
199
219
|
});
|
|
200
220
|
|
|
201
221
|
// Manual Encrypted JWT Tokens & AES-256-GCM Encryption
|
|
@@ -207,9 +227,36 @@ const decrypted = Auth.decrypt(encrypted, process.env.JWT_SECRET);
|
|
|
207
227
|
|
|
208
228
|
---
|
|
209
229
|
|
|
210
|
-
### 5.
|
|
230
|
+
### 5. HTTP Response Helpers & Health Check
|
|
231
|
+
|
|
232
|
+
Blue Bird enhances Express' `res` object with standardized helper methods:
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
// Standard Success Responses
|
|
236
|
+
res.ok(data, "Success"); // HTTP 200 { status: "success", message, data }
|
|
237
|
+
res.created(newItem, "Item created"); // HTTP 201 { status: "success", message, data }
|
|
238
|
+
res.paginate(items, pagination, "Fetched"); // HTTP 200 { status: "success", message, data, pagination }
|
|
239
|
+
|
|
240
|
+
// Standard Error Responses
|
|
241
|
+
res.badRequest("Invalid input", errors); // HTTP 400 { status: "error", message, errors }
|
|
242
|
+
res.unauthorized("Authentication required"); // HTTP 401 { status: "error", message }
|
|
243
|
+
res.forbidden("Access denied"); // HTTP 403 { status: "error", message }
|
|
244
|
+
res.notFound("Resource not found"); // HTTP 404 { status: "error", message }
|
|
245
|
+
res.serverError("Internal failure", err); // HTTP 500 { status: "error", message }
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### Health Check Endpoint (`/api/health`)
|
|
249
|
+
|
|
250
|
+
Every application includes an automatic `/api/health` route returning server status, uptime, environment, and memory consumption.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
### 6. Performance Cache & Modes (`Cache`)
|
|
211
255
|
|
|
212
|
-
|
|
256
|
+
Configured via `CACHE_MODE` in `.env`:
|
|
257
|
+
- `CACHE_MODE="memory"`: Fast local RAM cache inside Node.js with automated TTL cleanup (default when Redis is not used). Zero network overhead.
|
|
258
|
+
- `CACHE_MODE="redis"`: Distributed cache across Docker containers with automatic fallback to memory if Redis is unavailable.
|
|
259
|
+
- `CACHE_MODE="none"`: Caching disabled.
|
|
213
260
|
|
|
214
261
|
#### Route Caching Middleware
|
|
215
262
|
|
|
@@ -218,7 +265,7 @@ import Cache, { getRedisClient } from "@seip/blue-bird/core/cache.js";
|
|
|
218
265
|
|
|
219
266
|
// Cache endpoint for 60 seconds (sets X-Blue-Bird-Cache: HIT/MISS headers)
|
|
220
267
|
router.get("/stats", Cache.middleware(60), (req, res) => {
|
|
221
|
-
res.
|
|
268
|
+
res.ok({ usersOnline: 42 });
|
|
222
269
|
});
|
|
223
270
|
```
|
|
224
271
|
|
|
@@ -236,6 +283,7 @@ await Cache.delete("/api/public/config");
|
|
|
236
283
|
await Cache.clear();
|
|
237
284
|
```
|
|
238
285
|
|
|
286
|
+
|
|
239
287
|
#### Custom Database & Data Caching with `getRedisClient()`
|
|
240
288
|
|
|
241
289
|
```javascript
|
|
@@ -274,20 +322,29 @@ webRouter.use(App.helmet());
|
|
|
274
322
|
|
|
275
323
|
### 7. Database wrapper (`Database`)
|
|
276
324
|
|
|
277
|
-
Blue Bird provides a unified, multi-database client wrapper (`core/database.js`) supporting **
|
|
325
|
+
Blue Bird provides a unified, multi-database client wrapper (`core/database.js`) supporting **SQLite** (default), **MySQL**, and **PostgreSQL** with automated connection retry loops, query formatting utilities, and Redis query caching.
|
|
278
326
|
|
|
279
327
|
#### Driver Support
|
|
328
|
+
- **Native SQLite (`better-sqlite3`) [Default]**: Embedded, ultra-fast zero-latency database. Automatically applies `PRAGMA journal_mode = WAL;`, `PRAGMA busy_timeout = 5000;`, `PRAGMA synchronous = NORMAL;`, and `PRAGMA foreign_keys = ON;` to eliminate locking errors and support concurrent reader and writer operations.
|
|
280
329
|
- **Native MySQL (`mysql2/promise`)**: High-performance connection pool for MySQL 8.0+.
|
|
281
|
-
- **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
|
|
330
|
+
- **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 all database engines.
|
|
282
331
|
- **No Database (`none`)**: If no database is configured, the wrapper is disabled gracefully without crashing the server.
|
|
283
332
|
|
|
284
333
|
#### Standalone & Remote Database Configuration
|
|
285
|
-
You can connect to any local or remote database instance (outside Docker, such as Supabase, Neon, AWS RDS, or
|
|
334
|
+
You can connect to any local or remote database instance (outside Docker, such as Supabase, Neon, AWS RDS, local SQLite files, or MySQL/Postgres services) simply by defining the `DATABASE_URL` or `DB_FILE` in your `.env` file:
|
|
286
335
|
|
|
287
336
|
```env
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
337
|
+
# SQLite (Default)
|
|
338
|
+
DB_TYPE="sqlite"
|
|
339
|
+
DB_FILE="database/blue_bird.db"
|
|
340
|
+
DATABASE_URL="sqlite:database/blue_bird.db"
|
|
341
|
+
|
|
342
|
+
# PostgreSQL (Remote or local)
|
|
343
|
+
# DB_TYPE="postgres"
|
|
344
|
+
# DATABASE_URL="postgresql://postgres:password@localhost:5432/blue_bird?schema=public"
|
|
345
|
+
|
|
346
|
+
# MySQL (Remote or local)
|
|
347
|
+
# DB_TYPE="mysql"
|
|
291
348
|
# DATABASE_URL="mysql://root:password@localhost:3306/blue_bird"
|
|
292
349
|
```
|
|
293
350
|
|
|
@@ -296,16 +353,16 @@ DATABASE_URL="postgresql://postgres:password@localhost:5432/blue_bird?schema=pub
|
|
|
296
353
|
```javascript
|
|
297
354
|
import { Database, DB_TYPE } from "@seip/blue-bird/core/database.js";
|
|
298
355
|
|
|
299
|
-
// Instantiate the database connection pool
|
|
356
|
+
// Instantiate the database connection pool or SQLite instance
|
|
300
357
|
const connection = new Database(20);
|
|
301
358
|
|
|
302
|
-
// 1. Basic SELECT query returning single row (Works for
|
|
359
|
+
// 1. Basic SELECT query returning single row (Works for SQLite, MySQL, and PostgreSQL using ? placeholders)
|
|
303
360
|
const user = await connection.query("SELECT * FROM users WHERE email = ?", ["test@example.com"], "return_row");
|
|
304
361
|
|
|
305
362
|
// 2. Fetch rows with 60 seconds Redis caching enabled
|
|
306
363
|
const stats = await connection.query("SELECT COUNT(*) as count FROM access_logs", [], { cache: 60 });
|
|
307
364
|
|
|
308
|
-
// 3. INSERT query (returns insertId
|
|
365
|
+
// 3. INSERT query (returns insertId / lastInsertRowid across SQLite, MySQL, and PostgreSQL)
|
|
309
366
|
const newId = await connection.query("INSERT INTO users (name) VALUES (?)", ["John"]);
|
|
310
367
|
|
|
311
368
|
// 4. Automatic SQL Query Pagination (Runs count query + LIMIT/OFFSET calculation)
|
|
@@ -315,10 +372,10 @@ const paginated = await connection.paginate(
|
|
|
315
372
|
{ page: 1, limit: 10, cache: 60 }
|
|
316
373
|
);
|
|
317
374
|
|
|
318
|
-
// 5. Atomic Database Transactions with Automatic Commit & Rollback
|
|
375
|
+
// 5. Atomic Database Transactions with Automatic Commit & Rollback (uses BEGIN IMMEDIATE for SQLite)
|
|
319
376
|
const txUserId = await connection.transaction(async (tx) => {
|
|
320
377
|
const userId = await tx.query("INSERT INTO users (name, email) VALUES (?, ?)", ["Alice", "alice@example.com"]);
|
|
321
|
-
await tx.query("INSERT INTO profiles (user_id) VALUES (?)", [userId]);
|
|
378
|
+
await tx.query("INSERT INTO profiles (user_id) VALUES (?, ?)", [userId]);
|
|
322
379
|
return userId;
|
|
323
380
|
});
|
|
324
381
|
```
|
package/core/app.js
CHANGED
|
@@ -135,6 +135,34 @@ class App {
|
|
|
135
135
|
});
|
|
136
136
|
};
|
|
137
137
|
|
|
138
|
+
res.ok = (data = null, message = "Success") => {
|
|
139
|
+
return res.success(data, message, 200);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
res.created = (data = null, message = "Created") => {
|
|
143
|
+
return res.success(data, message, 201);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
res.badRequest = (message = "Bad Request", errors = []) => {
|
|
147
|
+
return res.error(message, 400, errors);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
res.unauthorized = (message = "Unauthorized") => {
|
|
151
|
+
return res.error(message, 401);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
res.forbidden = (message = "Forbidden") => {
|
|
155
|
+
return res.error(message, 403);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
res.notFound = (message = "Not Found") => {
|
|
159
|
+
return res.error(message, 404);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
res.serverError = (message = "Internal Server Error", errors = []) => {
|
|
163
|
+
return res.error(message, 500, errors);
|
|
164
|
+
};
|
|
165
|
+
|
|
138
166
|
res.paginate = (data = [], pagination = {}, message = "Success") => {
|
|
139
167
|
const page = Number(pagination.page) || 1;
|
|
140
168
|
const limit = Number(pagination.limit) || data.length;
|
|
@@ -157,6 +185,20 @@ class App {
|
|
|
157
185
|
next();
|
|
158
186
|
});
|
|
159
187
|
|
|
188
|
+
this.app.get("/api/health", (req, res) => {
|
|
189
|
+
return res.json({
|
|
190
|
+
status: "ok",
|
|
191
|
+
timestamp: new Date().toISOString(),
|
|
192
|
+
uptime: Math.floor(process.uptime()),
|
|
193
|
+
environment: props.debug ? "development" : "production",
|
|
194
|
+
memory: {
|
|
195
|
+
rss: `${Math.round(process.memoryUsage().rss / 1024 / 1024)}MB`,
|
|
196
|
+
heapUsed: `${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB`,
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
|
|
160
202
|
if (this.static.path || props.debug)
|
|
161
203
|
this.app.use(
|
|
162
204
|
express.static(path.join(__dirname, this.static.path || "frontend"), {
|