@seip/blue-bird 1.0.1 → 1.1.0
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 +1 -0
- package/AGENTS.md +9 -1
- package/README.md +14 -0
- package/core/cache.js +131 -0
- package/core/cli/docker.js +19 -19
- package/core/cli/init.js +44 -2
- package/core/index.d.ts +137 -132
- package/core/ws.js +210 -210
- package/frontend/js/utils.js +557 -557
- package/package.json +64 -65
package/.env_example
CHANGED
package/AGENTS.md
CHANGED
|
@@ -6,7 +6,7 @@ This document serves as the primary manual for any AI Agent interacting with the
|
|
|
6
6
|
|
|
7
7
|
Blue Bird is a performance-first API framework built on **Express**. It saves developers from repetitive configuration, validation, security, JWT authentication, and database environment configuration out of the box, delegating all static frontend rendering to Nginx.
|
|
8
8
|
|
|
9
|
-
- **Entrypoint (`index.js`)**: Initializes the server using `App` from `core/app.js` and registers the routes.
|
|
9
|
+
- **Entrypoint (`backend/index.js`)**: Initializes the server using `App` from `core/app.js` and registers the routes.
|
|
10
10
|
- **Backend (`backend/`)**: Application routes and logic (e.g. `backend/routes/`).
|
|
11
11
|
- **Frontend (`frontend/`)**: Static assets (HTML, CSS, JS). Handled directly by Nginx in production, bypassing Express.
|
|
12
12
|
- **Core (`core/`)**: The framework core. Contains wrapper classes such as `Router`, `Validator`, `Auth`, `Cache`, etc. **DO NOT MODIFY** the core unless explicitly requested, as it could break other apps.
|
|
@@ -87,9 +87,17 @@ If an Express route involves heavy processing or database queries, utilize the `
|
|
|
87
87
|
```javascript
|
|
88
88
|
import Cache from "@seip/blue-bird/core/cache.js";
|
|
89
89
|
|
|
90
|
+
// Express route middleware caching
|
|
90
91
|
router.get("/stats", Cache.middleware(60), (req, res) => {
|
|
91
92
|
res.json({ ok: true });
|
|
92
93
|
});
|
|
94
|
+
|
|
95
|
+
// Programmatic cache manipulation
|
|
96
|
+
await Cache.set("custom_key", { data: "value" }, 120);
|
|
97
|
+
const cachedData = await Cache.get("custom_key");
|
|
98
|
+
|
|
99
|
+
// Invalidate route cache manually (e.g. after updating DB)
|
|
100
|
+
await Cache.delete("/api/public/config");
|
|
93
101
|
```
|
|
94
102
|
|
|
95
103
|
The Cache module integrates with Redis when `REDIS_HOST` is configured in the environment. If Redis is unavailable or fails, it transparently falls back to an in-memory cache system without interrupting requests.
|
package/README.md
CHANGED
|
@@ -222,6 +222,20 @@ router.get("/stats", Cache.middleware(60), (req, res) => {
|
|
|
222
222
|
});
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
+
#### Programmatic Cache Manipulation & Invalidation
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
// Get / Set keys programmatically
|
|
229
|
+
await Cache.set("custom_key", { data: "value" }, 120);
|
|
230
|
+
const cachedData = await Cache.get("custom_key");
|
|
231
|
+
|
|
232
|
+
// Manually invalidate route cache (e.g. after updating DB)
|
|
233
|
+
await Cache.delete("/api/public/config");
|
|
234
|
+
|
|
235
|
+
// Clear all cache entries
|
|
236
|
+
await Cache.clear();
|
|
237
|
+
```
|
|
238
|
+
|
|
225
239
|
#### Custom Database & Data Caching with `getRedisClient()`
|
|
226
240
|
|
|
227
241
|
```javascript
|
package/core/cache.js
CHANGED
|
@@ -162,6 +162,137 @@ class Cache {
|
|
|
162
162
|
next();
|
|
163
163
|
};
|
|
164
164
|
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Retrieves cached value by key.
|
|
168
|
+
* @param {string} key - Cache key.
|
|
169
|
+
* @returns {Promise<any|null>} Cached payload or null.
|
|
170
|
+
*/
|
|
171
|
+
static async get(key) {
|
|
172
|
+
key = key.trim();
|
|
173
|
+
if (!key) return null;
|
|
174
|
+
|
|
175
|
+
if (redisHost && !redisClient) {
|
|
176
|
+
await initRedis().catch(() => { });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (isRedisConnected && redisClient) {
|
|
180
|
+
try {
|
|
181
|
+
const cachedData = await redisClient.get(key);
|
|
182
|
+
if (cachedData) {
|
|
183
|
+
try {
|
|
184
|
+
const cached = JSON.parse(cachedData);
|
|
185
|
+
return cached && typeof cached === "object" && "data" in cached ? cached.data : cached;
|
|
186
|
+
} catch {
|
|
187
|
+
return cachedData;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
} catch (err) {
|
|
192
|
+
isRedisConnected = false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (CACHE[key]) {
|
|
197
|
+
if (CACHE[key].expiry > Date.now()) {
|
|
198
|
+
const cached = CACHE[key];
|
|
199
|
+
return cached.data !== undefined ? cached.data : cached;
|
|
200
|
+
}
|
|
201
|
+
delete CACHE[key];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Sets data into cache with a specified TTL in seconds.
|
|
209
|
+
* @param {string} key - Cache key.
|
|
210
|
+
* @param {any} value - Data to cache.
|
|
211
|
+
* @param {number} [seconds=60] - Expiry time in seconds.
|
|
212
|
+
* @returns {Promise<boolean>} True if set successfully.
|
|
213
|
+
*/
|
|
214
|
+
static async set(key, value, seconds = 60) {
|
|
215
|
+
key = key.trim();
|
|
216
|
+
if (!key) return false;
|
|
217
|
+
|
|
218
|
+
if (redisHost && !redisClient) {
|
|
219
|
+
await initRedis().catch(() => { });
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const cacheObject = {
|
|
223
|
+
type: typeof value === "string" ? "html" : "json",
|
|
224
|
+
data: value,
|
|
225
|
+
expiry: Date.now() + seconds * 1000,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
if (isRedisConnected && redisClient) {
|
|
229
|
+
try {
|
|
230
|
+
await redisClient.set(key, JSON.stringify(cacheObject), {
|
|
231
|
+
EX: seconds,
|
|
232
|
+
});
|
|
233
|
+
} catch (err) {
|
|
234
|
+
CACHE[key] = cacheObject;
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
CACHE[key] = cacheObject;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Deletes one or more entries from cache.
|
|
245
|
+
* @param {string|string[]} keys - Single key or array of keys to delete.
|
|
246
|
+
* @returns {Promise<boolean>} True if deleted.
|
|
247
|
+
*/
|
|
248
|
+
static async delete(keys) {
|
|
249
|
+
if (!keys) return false;
|
|
250
|
+
const keyList = Array.isArray(keys) ? keys : [keys];
|
|
251
|
+
|
|
252
|
+
if (redisHost && !redisClient) {
|
|
253
|
+
await initRedis().catch(() => { });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
for (const key of keyList) {
|
|
257
|
+
delete CACHE[key];
|
|
258
|
+
if (isRedisConnected && redisClient) {
|
|
259
|
+
try {
|
|
260
|
+
await redisClient.del(key);
|
|
261
|
+
} catch (err) {
|
|
262
|
+
isRedisConnected = false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Alias for delete.
|
|
272
|
+
* @param {string|string[]} keys - Single key or array of keys to delete.
|
|
273
|
+
* @returns {Promise<boolean>} True if deleted.
|
|
274
|
+
*/
|
|
275
|
+
static async del(keys) {
|
|
276
|
+
return this.delete(keys);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Flushes all cached data in memory (and Redis if connected).
|
|
281
|
+
* @returns {Promise<boolean>} True if flushed.
|
|
282
|
+
*/
|
|
283
|
+
static async clear() {
|
|
284
|
+
for (const key in CACHE) {
|
|
285
|
+
delete CACHE[key];
|
|
286
|
+
}
|
|
287
|
+
if (isRedisConnected && redisClient) {
|
|
288
|
+
try {
|
|
289
|
+
await redisClient.flushDb();
|
|
290
|
+
} catch (err) {
|
|
291
|
+
isRedisConnected = false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
165
296
|
}
|
|
166
297
|
|
|
167
298
|
/**
|
package/core/cli/docker.js
CHANGED
|
@@ -145,25 +145,25 @@ async function startCommand(service) {
|
|
|
145
145
|
/**
|
|
146
146
|
* Handles the 'dev' CLI command.
|
|
147
147
|
*/
|
|
148
|
-
async function devCommand() {
|
|
149
|
-
checkComposeFile();
|
|
150
|
-
const dbType = getDbType();
|
|
151
|
-
console.log(chalk.cyan("Starting development environment (Database + Redis)..."));
|
|
152
|
-
|
|
153
|
-
const containers = ["redis"];
|
|
154
|
-
if (dbType !== "none") {
|
|
155
|
-
containers.unshift(dbType);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const code = await runCmd("docker", ["compose", "up", "-d", ...containers]);
|
|
159
|
-
if (code === 0) {
|
|
160
|
-
console.log(chalk.green("Development containers started."));
|
|
161
|
-
console.log(chalk.cyan("View live logs with: npx blue-bird docker logs db"));
|
|
162
|
-
console.log(chalk.yellow("Now execute: npm run dev"));
|
|
163
|
-
} else {
|
|
164
|
-
console.error(chalk.red("Error starting development environment."));
|
|
165
|
-
process.exit(1);
|
|
166
|
-
}
|
|
148
|
+
async function devCommand() {
|
|
149
|
+
checkComposeFile();
|
|
150
|
+
const dbType = getDbType();
|
|
151
|
+
console.log(chalk.cyan("Starting development environment (Database + Redis)..."));
|
|
152
|
+
|
|
153
|
+
const containers = ["redis"];
|
|
154
|
+
if (dbType !== "none") {
|
|
155
|
+
containers.unshift(dbType);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const code = await runCmd("docker", ["compose", "up", "-d", ...containers]);
|
|
159
|
+
if (code === 0) {
|
|
160
|
+
console.log(chalk.green("Development containers started."));
|
|
161
|
+
console.log(chalk.cyan("View live logs with: npx blue-bird docker logs db"));
|
|
162
|
+
console.log(chalk.yellow("Now execute: npm run dev"));
|
|
163
|
+
} else {
|
|
164
|
+
console.error(chalk.red("Error starting development environment."));
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
/**
|
package/core/cli/init.js
CHANGED
|
@@ -103,8 +103,6 @@ class ProjectInit {
|
|
|
103
103
|
"docker",
|
|
104
104
|
".env_example",
|
|
105
105
|
"AGENTS.md",
|
|
106
|
-
"index.js",
|
|
107
|
-
".gitignore"
|
|
108
106
|
];
|
|
109
107
|
|
|
110
108
|
try {
|
|
@@ -128,6 +126,24 @@ class ProjectInit {
|
|
|
128
126
|
}
|
|
129
127
|
});
|
|
130
128
|
|
|
129
|
+
const gitignoreContent = `node_modules\nlogs\n.env\npackage-lock.json\n*.db\ntest/\n\nbackups/*.sql\n`;
|
|
130
|
+
const gitignoreDest = path.join(this.appDir, ".gitignore");
|
|
131
|
+
const gitignoreSrc = path.join(this.sourceDir, ".gitignore");
|
|
132
|
+
|
|
133
|
+
if (!fs.existsSync(gitignoreDest)) {
|
|
134
|
+
if (fs.existsSync(gitignoreSrc)) {
|
|
135
|
+
fs.copyFileSync(gitignoreSrc, gitignoreDest);
|
|
136
|
+
console.log(chalk.green("[OK] Copied .gitignore to root."));
|
|
137
|
+
} else {
|
|
138
|
+
fs.writeFileSync(gitignoreDest, gitignoreContent, "utf-8");
|
|
139
|
+
console.log(chalk.green("[OK] Created .gitignore file."));
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
console.log(
|
|
143
|
+
chalk.yellow("[SKIP] .gitignore already exists, skipping."),
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
131
147
|
const composeTemplateName =
|
|
132
148
|
dbType === "postgres"
|
|
133
149
|
? "docker-compose.postgres.yml"
|
|
@@ -166,8 +182,15 @@ class ProjectInit {
|
|
|
166
182
|
let envContent = fs.readFileSync(envExamplePath, "utf-8");
|
|
167
183
|
|
|
168
184
|
const jwtSecret = crypto.randomBytes(32).toString("hex");
|
|
185
|
+
const composeProjectName =
|
|
186
|
+
title
|
|
187
|
+
.toLowerCase()
|
|
188
|
+
.trim()
|
|
189
|
+
.replace(/\s+/g, "-")
|
|
190
|
+
.replace(/[^a-z0-9_-]/g, "") || "blue-bird";
|
|
169
191
|
|
|
170
192
|
const updates = {
|
|
193
|
+
COMPOSE_PROJECT_NAME: composeProjectName,
|
|
171
194
|
TITLE: title,
|
|
172
195
|
PORT: port,
|
|
173
196
|
APP_URL: appUrl,
|
|
@@ -190,10 +213,14 @@ class ProjectInit {
|
|
|
190
213
|
}
|
|
191
214
|
|
|
192
215
|
const lines = envContent.split(/\r?\n/);
|
|
216
|
+
let foundComposeProjectName = false;
|
|
193
217
|
const updatedLines = lines.map((line) => {
|
|
194
218
|
const match = line.match(/^([A-Z_]+)=(.+)/);
|
|
195
219
|
if (match) {
|
|
196
220
|
const key = match[1];
|
|
221
|
+
if (key === "COMPOSE_PROJECT_NAME") {
|
|
222
|
+
foundComposeProjectName = true;
|
|
223
|
+
}
|
|
197
224
|
if (updates[key] !== undefined) {
|
|
198
225
|
const value = updates[key];
|
|
199
226
|
if (typeof value === "string" && !value.startsWith('"')) {
|
|
@@ -204,6 +231,21 @@ class ProjectInit {
|
|
|
204
231
|
}
|
|
205
232
|
return line;
|
|
206
233
|
});
|
|
234
|
+
|
|
235
|
+
if (!foundComposeProjectName && updates.COMPOSE_PROJECT_NAME) {
|
|
236
|
+
const titleIdx = updatedLines.findIndex((l) => l.startsWith("TITLE="));
|
|
237
|
+
if (titleIdx !== -1) {
|
|
238
|
+
updatedLines.splice(
|
|
239
|
+
titleIdx,
|
|
240
|
+
0,
|
|
241
|
+
`COMPOSE_PROJECT_NAME="${updates.COMPOSE_PROJECT_NAME}"`,
|
|
242
|
+
);
|
|
243
|
+
} else {
|
|
244
|
+
updatedLines.push(
|
|
245
|
+
`COMPOSE_PROJECT_NAME="${updates.COMPOSE_PROJECT_NAME}"`,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
207
249
|
envContent = updatedLines.join("\n");
|
|
208
250
|
|
|
209
251
|
fs.writeFileSync(envPath, envContent, "utf-8");
|
package/core/index.d.ts
CHANGED
|
@@ -1,132 +1,137 @@
|
|
|
1
|
-
import { Router as ExpressRouter, Request, Response, NextFunction } from "express";
|
|
2
|
-
|
|
3
|
-
declare global {
|
|
4
|
-
namespace Express {
|
|
5
|
-
interface Response {
|
|
6
|
-
/**
|
|
7
|
-
* Sends a standardized JSON success response.
|
|
8
|
-
* @param data Data payload to return.
|
|
9
|
-
* @param message Success message.
|
|
10
|
-
* @param statusCode HTTP status code (default: 200).
|
|
11
|
-
*/
|
|
12
|
-
success(data?: any, message?: string, statusCode?: number): Response;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Sends a standardized JSON error response.
|
|
16
|
-
* @param message Error message.
|
|
17
|
-
* @param statusCode HTTP status code (default: 400).
|
|
18
|
-
* @param errors Array or object of detailed errors.
|
|
19
|
-
*/
|
|
20
|
-
error(message?: string, statusCode?: number, errors?: any): Response;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Sends a standardized paginated JSON response.
|
|
24
|
-
* @param data Array of records for current page.
|
|
25
|
-
* @param pagination Object containing page, limit, and total count.
|
|
26
|
-
* @param message Success message.
|
|
27
|
-
*/
|
|
28
|
-
paginate(
|
|
29
|
-
data?: any[],
|
|
30
|
-
pagination?: { page?: number; limit?: number; total?: number },
|
|
31
|
-
message?: string
|
|
32
|
-
): Response;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export class AppError extends Error {
|
|
38
|
-
statusCode: number;
|
|
39
|
-
errors: any;
|
|
40
|
-
isOperational: boolean;
|
|
41
|
-
|
|
42
|
-
constructor(message: string, statusCode?: number, errors?: any);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export class App {
|
|
46
|
-
constructor(options?: {
|
|
47
|
-
routes?: any[];
|
|
48
|
-
cors?: any;
|
|
49
|
-
middlewares?: any[];
|
|
50
|
-
port?: number | string;
|
|
51
|
-
host?: string;
|
|
52
|
-
logger?: boolean;
|
|
53
|
-
notFound?: boolean;
|
|
54
|
-
json?: boolean;
|
|
55
|
-
urlencoded?: boolean;
|
|
56
|
-
static?: { path: string; options?: any };
|
|
57
|
-
cookieParser?: boolean;
|
|
58
|
-
rateLimit?: boolean | any;
|
|
59
|
-
swagger?: boolean | any;
|
|
60
|
-
compression?: boolean;
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
use(record: any): void;
|
|
64
|
-
set(key: string, value: any): void;
|
|
65
|
-
websocket(
|
|
66
|
-
options?:
|
|
67
|
-
| ((ws: any, req: any) => void)
|
|
68
|
-
| { path?: string; auth?: boolean }
|
|
69
|
-
): WebSocketManager;
|
|
70
|
-
run(): void;
|
|
71
|
-
|
|
72
|
-
static helmet(options?: any): any;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export class WebSocketManager {
|
|
76
|
-
constructor(server: any, options?: { path?: string; auth?: boolean });
|
|
77
|
-
onConnection(handler: (ws: any, req: any) => void): void;
|
|
78
|
-
join(room: string, ws: any): void;
|
|
79
|
-
leave(room: string, ws: any): void;
|
|
80
|
-
broadcast(data: any, room?: string | null): void;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export class Router {
|
|
84
|
-
constructor(path?: string, options?: { seo?: boolean; languages?: string[] });
|
|
85
|
-
|
|
86
|
-
use(...middleware: any[]): void;
|
|
87
|
-
get(path: string | RegExp, ...callback: any[]): void;
|
|
88
|
-
post(path: string | RegExp, ...callback: any[]): void;
|
|
89
|
-
put(path: string | RegExp, ...callback: any[]): void;
|
|
90
|
-
delete(path: string | RegExp, ...callback: any[]): void;
|
|
91
|
-
patch(path: string | RegExp, ...callback: any[]): void;
|
|
92
|
-
options(path: string | RegExp, ...callback: any[]): void;
|
|
93
|
-
getRouter(): ExpressRouter;
|
|
94
|
-
getPath(): string;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export class Validator {
|
|
98
|
-
constructor(schema: Record<string, any>, lang?: string);
|
|
99
|
-
middleware(): (req: Request, res: Response, next: NextFunction) => void;
|
|
100
|
-
validate(data: Record<string, any>): { valid: boolean; errors: any[] };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export class Auth {
|
|
104
|
-
static encrypt(payload: any, secret: string): string;
|
|
105
|
-
static decrypt(data: string, secret: string): any;
|
|
106
|
-
static generateToken(payload: any, secret?: string, expiresIn?: string | number): string;
|
|
107
|
-
static verifyToken(token: string, secret?: string): any;
|
|
108
|
-
static protect(options?: { redirect?: string | null; key?: string; cookieKey?: string }): (req: Request, res: Response, next: NextFunction) => Promise<any>;
|
|
109
|
-
static login(res: Response, data: any, key?: string, options?: { expiresIn?: string | number; cookie?: any }): Promise<string>;
|
|
110
|
-
static logout(res: Response, key?: string, options?: any, req?: Request): Promise<boolean>;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export class Cache {
|
|
114
|
-
static middleware(seconds?: number): (req: Request, res: Response, next: NextFunction) => Promise<any>;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
1
|
+
import { Router as ExpressRouter, Request, Response, NextFunction } from "express";
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
namespace Express {
|
|
5
|
+
interface Response {
|
|
6
|
+
/**
|
|
7
|
+
* Sends a standardized JSON success response.
|
|
8
|
+
* @param data Data payload to return.
|
|
9
|
+
* @param message Success message.
|
|
10
|
+
* @param statusCode HTTP status code (default: 200).
|
|
11
|
+
*/
|
|
12
|
+
success(data?: any, message?: string, statusCode?: number): Response;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Sends a standardized JSON error response.
|
|
16
|
+
* @param message Error message.
|
|
17
|
+
* @param statusCode HTTP status code (default: 400).
|
|
18
|
+
* @param errors Array or object of detailed errors.
|
|
19
|
+
*/
|
|
20
|
+
error(message?: string, statusCode?: number, errors?: any): Response;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Sends a standardized paginated JSON response.
|
|
24
|
+
* @param data Array of records for current page.
|
|
25
|
+
* @param pagination Object containing page, limit, and total count.
|
|
26
|
+
* @param message Success message.
|
|
27
|
+
*/
|
|
28
|
+
paginate(
|
|
29
|
+
data?: any[],
|
|
30
|
+
pagination?: { page?: number; limit?: number; total?: number },
|
|
31
|
+
message?: string
|
|
32
|
+
): Response;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class AppError extends Error {
|
|
38
|
+
statusCode: number;
|
|
39
|
+
errors: any;
|
|
40
|
+
isOperational: boolean;
|
|
41
|
+
|
|
42
|
+
constructor(message: string, statusCode?: number, errors?: any);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class App {
|
|
46
|
+
constructor(options?: {
|
|
47
|
+
routes?: any[];
|
|
48
|
+
cors?: any;
|
|
49
|
+
middlewares?: any[];
|
|
50
|
+
port?: number | string;
|
|
51
|
+
host?: string;
|
|
52
|
+
logger?: boolean;
|
|
53
|
+
notFound?: boolean;
|
|
54
|
+
json?: boolean;
|
|
55
|
+
urlencoded?: boolean;
|
|
56
|
+
static?: { path: string; options?: any };
|
|
57
|
+
cookieParser?: boolean;
|
|
58
|
+
rateLimit?: boolean | any;
|
|
59
|
+
swagger?: boolean | any;
|
|
60
|
+
compression?: boolean;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
use(record: any): void;
|
|
64
|
+
set(key: string, value: any): void;
|
|
65
|
+
websocket(
|
|
66
|
+
options?:
|
|
67
|
+
| ((ws: any, req: any) => void)
|
|
68
|
+
| { path?: string; auth?: boolean }
|
|
69
|
+
): WebSocketManager;
|
|
70
|
+
run(): void;
|
|
71
|
+
|
|
72
|
+
static helmet(options?: any): any;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class WebSocketManager {
|
|
76
|
+
constructor(server: any, options?: { path?: string; auth?: boolean });
|
|
77
|
+
onConnection(handler: (ws: any, req: any) => void): void;
|
|
78
|
+
join(room: string, ws: any): void;
|
|
79
|
+
leave(room: string, ws: any): void;
|
|
80
|
+
broadcast(data: any, room?: string | null): void;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export class Router {
|
|
84
|
+
constructor(path?: string, options?: { seo?: boolean; languages?: string[] });
|
|
85
|
+
|
|
86
|
+
use(...middleware: any[]): void;
|
|
87
|
+
get(path: string | RegExp, ...callback: any[]): void;
|
|
88
|
+
post(path: string | RegExp, ...callback: any[]): void;
|
|
89
|
+
put(path: string | RegExp, ...callback: any[]): void;
|
|
90
|
+
delete(path: string | RegExp, ...callback: any[]): void;
|
|
91
|
+
patch(path: string | RegExp, ...callback: any[]): void;
|
|
92
|
+
options(path: string | RegExp, ...callback: any[]): void;
|
|
93
|
+
getRouter(): ExpressRouter;
|
|
94
|
+
getPath(): string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export class Validator {
|
|
98
|
+
constructor(schema: Record<string, any>, lang?: string);
|
|
99
|
+
middleware(): (req: Request, res: Response, next: NextFunction) => void;
|
|
100
|
+
validate(data: Record<string, any>): { valid: boolean; errors: any[] };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export class Auth {
|
|
104
|
+
static encrypt(payload: any, secret: string): string;
|
|
105
|
+
static decrypt(data: string, secret: string): any;
|
|
106
|
+
static generateToken(payload: any, secret?: string, expiresIn?: string | number): string;
|
|
107
|
+
static verifyToken(token: string, secret?: string): any;
|
|
108
|
+
static protect(options?: { redirect?: string | null; key?: string; cookieKey?: string }): (req: Request, res: Response, next: NextFunction) => Promise<any>;
|
|
109
|
+
static login(res: Response, data: any, key?: string, options?: { expiresIn?: string | number; cookie?: any }): Promise<string>;
|
|
110
|
+
static logout(res: Response, key?: string, options?: any, req?: Request): Promise<boolean>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export class Cache {
|
|
114
|
+
static middleware(seconds?: number): (req: Request, res: Response, next: NextFunction) => Promise<any>;
|
|
115
|
+
static get(key: string): Promise<any | null>;
|
|
116
|
+
static set(key: string, value: any, seconds?: number): Promise<boolean>;
|
|
117
|
+
static delete(keys: string | string[]): Promise<boolean>;
|
|
118
|
+
static del(keys: string | string[]): Promise<boolean>;
|
|
119
|
+
static clear(): Promise<boolean>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function getRedisClient(): any;
|
|
123
|
+
|
|
124
|
+
export class Database {
|
|
125
|
+
constructor(connectionLimit?: number, queueLimit?: number, config?: any);
|
|
126
|
+
init(retries?: number): Promise<boolean>;
|
|
127
|
+
query(sql: string, params?: any[], options?: any): Promise<any>;
|
|
128
|
+
paginate(
|
|
129
|
+
sql: string,
|
|
130
|
+
params?: any[],
|
|
131
|
+
options?: { page?: number; limit?: number; cache?: number }
|
|
132
|
+
): Promise<{ data: any[]; total: number; page: number; limit: number; totalPages: number }>;
|
|
133
|
+
transaction<T = any>(callback: (tx: { query: (sql: string, params?: any[], options?: any) => Promise<any> }) => Promise<T>): Promise<T>;
|
|
134
|
+
executeTransaction<T = any>(callback: (tx: { query: (sql: string, params?: any[], options?: any) => Promise<any> }) => Promise<T>): Promise<T>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default App;
|