beech-api 3.9.0-beta.8-rc → 3.9.0-beta.9-rc
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/README.md +69 -32
- package/package.json +3 -2
- package/packages/cli/bin/beech-app.js +1 -1
- package/packages/cli/bin/beech-service.js +3 -2
- package/packages/cli/core/auth/Credentials.js +80 -58
- package/packages/cli/core/databases/test.js +1 -1
- package/packages/cli/core/generator/_package +5 -6
- package/packages/cli/core/generator/_scheduler +16 -6
- package/packages/cli/core/generator/index.js +80 -0
- package/packages/cli/core/index.js +80 -7
- package/packages/cli/core/middleware/express/duplicateRequest.js +10 -6
- package/packages/cli/core/middleware/express/jwtCheckAllow.js +3 -3
- package/packages/cli/core/middleware/express/rateLimit.js +14 -2
- package/packages/cli/core/services/http.express.js +49 -7
- package/packages/cli/core/test/check-node.js +21 -0
- package/packages/lib/src/endpoint.js +344 -194
package/README.md
CHANGED
|
@@ -17,13 +17,13 @@ The Beech API is API framework, It's help you with very easy to create API proje
|
|
|
17
17
|
- <b>[Beech CLI tool available](#beech-cli-tool-available)</b>
|
|
18
18
|
- ✨ <b>Automation Endpoints with CRUD</b>
|
|
19
19
|
- [Database Connection](#database-connection)
|
|
20
|
-
- [Model](#
|
|
20
|
+
- [Model](#models)
|
|
21
21
|
- [Retrieving data with Query String](#retrieving-data-with-query-string)
|
|
22
22
|
- Conditions
|
|
23
23
|
- Grouping
|
|
24
24
|
- Ordering
|
|
25
25
|
- Timestamps (Add-on in Store and Update)
|
|
26
|
-
- [Transactions](
|
|
26
|
+
- [Transactions](#-transactions)
|
|
27
27
|
- [Disorganized transactions](#way-1---disorganized-transactions-)
|
|
28
28
|
- [Organized transactions](#way-2---organized-transactions-)
|
|
29
29
|
- [Transactions set Isolation levels](#way-3---transactions-set-isolation-levels-)
|
|
@@ -80,8 +80,11 @@ $ yarn global add beech-api
|
|
|
80
80
|
|
|
81
81
|
Installation demo:
|
|
82
82
|
|
|
83
|
-
[Demo](https://i.ibb.co/
|
|
84
|
-

|
|
84
|
+

|
|
85
|
+
|
|
86
|
+
[Demo 2](https://i.ibb.co/wrdgyzDP/ezgif-8539024298ec62a9.gif)
|
|
87
|
+

|
|
85
88
|
|
|
86
89
|
After installation, you will have access to the `beech-app` binary in your command line.
|
|
87
90
|
You can check you have the right version with this command:
|
|
@@ -179,7 +182,7 @@ Output: FjgcgJPylkV7EeQJjea_EeifPwaHVO9onD3ATk3YYAyvjtMGu3dcDS0ejA
|
|
|
179
182
|
|
|
180
183
|
📂 app.config.js
|
|
181
184
|
```js
|
|
182
|
-
//
|
|
185
|
+
// Database configuration Only Basic & Sequelize (needed Hash)
|
|
183
186
|
|
|
184
187
|
...
|
|
185
188
|
|
|
@@ -217,6 +220,8 @@ database_config: [
|
|
|
217
220
|
```
|
|
218
221
|
❓ **Caution! :** Every re-new generate `app_key`. Must to new Hash your Access and change to ALL Database connections.
|
|
219
222
|
|
|
223
|
+
❓ **Development** : You can add ```dev: true``` in ```main_config``` for a better experience.
|
|
224
|
+
|
|
220
225
|
# Models
|
|
221
226
|
|
|
222
227
|
The `models` keep the files of function(s) data managemnets for Retriving, Creating, Updating and Destroying (CRUD). for understanding you might make model name same your table name inside `src/models` folder.
|
|
@@ -325,8 +330,7 @@ Fruit.options = {
|
|
|
325
330
|
// Option 2: Allow with specific per methods
|
|
326
331
|
defaultEndpoint: {
|
|
327
332
|
GET: true,
|
|
328
|
-
POST:
|
|
329
|
-
PATCH: {
|
|
333
|
+
POST: {
|
|
330
334
|
allow: true, // allow Auto-Endpoint
|
|
331
335
|
jwt: {
|
|
332
336
|
allow: true, // allow JWT
|
|
@@ -334,7 +338,19 @@ Fruit.options = {
|
|
|
334
338
|
{ role: [1, 2] },
|
|
335
339
|
],
|
|
336
340
|
},
|
|
341
|
+
// Rate Limit for POST
|
|
342
|
+
rate_limit: {
|
|
343
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
344
|
+
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
|
|
345
|
+
// store: ... , // Redis, Memcached, etc.
|
|
346
|
+
// See more: https://www.npmjs.com/package/express-rate-limit#Configuration
|
|
347
|
+
},
|
|
348
|
+
// Duplicate Request for POST
|
|
349
|
+
duplicate_request: {
|
|
350
|
+
expiration: 500, // Can't duplicate request for 5 milliseconds each IP requests per `window` (Disabled for Set expiration to 0 zero.)
|
|
351
|
+
},
|
|
337
352
|
},
|
|
353
|
+
PATCH: false,
|
|
338
354
|
DELETE: false,
|
|
339
355
|
},
|
|
340
356
|
|
|
@@ -379,16 +395,17 @@ Now you can add Query String with Conditional, Grouping and Ordering (Now Suppor
|
|
|
379
395
|
|
|
380
396
|
<b style="font-size:12pt">For Example, Now!</b>, You can request to `/fruit` with methods GET, POST, PATCH and DELETE like this.
|
|
381
397
|
|
|
382
|
-
| Efficacy | Method | Endpoint | Body |
|
|
383
|
-
|
|
384
|
-
| Create | POST | /fruit | { }
|
|
385
|
-
|
|
|
386
|
-
| Read | GET | /fruit
|
|
387
|
-
| Read | GET | /fruit
|
|
388
|
-
| Read | GET | /fruit?
|
|
389
|
-
| Read | GET | /fruit?
|
|
390
|
-
|
|
|
391
|
-
|
|
|
398
|
+
| Efficacy | Method | Endpoint | Body | Mode |
|
|
399
|
+
|:---------|:---------|:-----------------------|:-------------|:--------|
|
|
400
|
+
| Create | POST | /fruit | { } | Single |
|
|
401
|
+
| Create | POST | /fruit | [ ] | Bulk |
|
|
402
|
+
| Read | GET | /fruit | No | Fetch |
|
|
403
|
+
| Read | GET | /fruit/:limit/:offset | No | Fetch |
|
|
404
|
+
| Read | GET | /fruit?someField=1 | No | Fetch |
|
|
405
|
+
| Read | GET | /fruit?orderby=sort | No | Fetch |
|
|
406
|
+
| Read | GET | /fruit?groupby=id | No | Fetch |
|
|
407
|
+
| Update | PATCH | /fruit/:id | { } | Single |
|
|
408
|
+
| Delete | DELETE | /fruit/:id | No | Single |
|
|
392
409
|
|
|
393
410
|
Add some Basic Conditions, Grouping and Ordering with `QUERY STRING` under GET methods<br/>
|
|
394
411
|
|
|
@@ -627,15 +644,15 @@ The **JWT Broken Role** mechanism provides a flexible way to bypass or override
|
|
|
627
644
|
|
|
628
645
|
#### It can be applied in three different levels, depending on your use case.
|
|
629
646
|
|
|
630
|
-
| Level
|
|
631
|
-
|
|
632
|
-
| Global (``passport.config.js``)
|
|
633
|
-
| Model-level options | Per model & HTTP method | Structured, reusable rules |
|
|
634
|
-
| Endpoint middleware | Single endpoint | Custom or special cases |
|
|
647
|
+
| Priority | Level | Scope | Best For |
|
|
648
|
+
|:---------|:--------------------------------|:-------------------------|:--------------------------------|
|
|
649
|
+
| 3rd | Global (``passport.config.js``) | All endpoints | Common authorization exceptions |
|
|
650
|
+
| 2nd | Model-level options | Per model & HTTP method | Structured, reusable rules |
|
|
651
|
+
| 1st | Endpoint middleware | Single endpoint | Custom or special cases |
|
|
635
652
|
|
|
636
653
|
This multi-layer approach allows you to design **secure, flexible, and maintainable JWT authorization flows**.
|
|
637
654
|
|
|
638
|
-
### 1. Global Configuration (passport.config.js)
|
|
655
|
+
### 1. Global Configuration (passport.config.js) - Priority 3
|
|
639
656
|
|
|
640
657
|
You can define global JWT Broken Role rules that apply to all endpoints by configuring them in ``passport.config.js``.
|
|
641
658
|
```js
|
|
@@ -667,7 +684,7 @@ module.exports = {
|
|
|
667
684
|
- Apply common authorization exceptions across the entire application
|
|
668
685
|
- Support both simple role arrays and advanced operators (``$in``, ``$regex``, etc.)
|
|
669
686
|
|
|
670
|
-
### 2. Model-Level Configuration (Per HTTP Method)
|
|
687
|
+
### 2. Model-Level Configuration (Per HTTP Method) - Priority 2
|
|
671
688
|
|
|
672
689
|
You can configure JWT and Broken Role rules per model and per HTTP method using model options.
|
|
673
690
|
|
|
@@ -714,7 +731,7 @@ Fruit.options = {
|
|
|
714
731
|
- Different role requirements for POST, PATCH, etc.
|
|
715
732
|
- Combine role-based and attribute-based conditions
|
|
716
733
|
|
|
717
|
-
### 3. Endpoint-Level Configuration (Credentials Middleware)
|
|
734
|
+
### 3. Endpoint-Level Configuration (Credentials Middleware) - Priority 1
|
|
718
735
|
|
|
719
736
|
For maximum flexibility, you can define Broken Role rules directly on a specific endpoint using the Credentials middleware.
|
|
720
737
|
|
|
@@ -782,8 +799,8 @@ Credentials([
|
|
|
782
799
|
// Common Matching Patterns
|
|
783
800
|
{ role: [1, 2, 3] } // IN
|
|
784
801
|
{ role: 1 } // Equal
|
|
785
|
-
{ email: 'a@b.com' }
|
|
786
|
-
{ department: 'IT' }
|
|
802
|
+
{ email: 'a@b.com' } // Equal
|
|
803
|
+
{ department: 'IT' } // Equal
|
|
787
804
|
{ status: ['active'] } // Dynamic key
|
|
788
805
|
```
|
|
789
806
|
|
|
@@ -959,8 +976,9 @@ module.exports = {
|
|
|
959
976
|
When you config passport with ```users``` table already. You will got Auth endpoint in available.
|
|
960
977
|
```js
|
|
961
978
|
POST: "/authentication" // Request token
|
|
979
|
+
POST: "/authentication/refresh" // Request refresh token
|
|
962
980
|
POST: "/authentication/create" // Create new Auth data
|
|
963
|
-
PATCH: "/authentication/update/:id" // Update old Auth data (needed id)
|
|
981
|
+
PATCH: "/authentication/update/:id" // Update old Auth data (needed user id)
|
|
964
982
|
```
|
|
965
983
|
|
|
966
984
|
***XHR Example :***
|
|
@@ -968,26 +986,28 @@ PATCH: "/authentication/update/:id" // Update old Auth data (needed id)
|
|
|
968
986
|
```js
|
|
969
987
|
// Request with body for gether Token
|
|
970
988
|
POST: "/authentication"
|
|
971
|
-
{
|
|
989
|
+
payload: {
|
|
972
990
|
username: "bombkiml",
|
|
973
991
|
password: "secret"
|
|
974
992
|
}
|
|
975
993
|
|
|
994
|
+
// Request with header for refresh token
|
|
995
|
+
POST: "/authentication/refresh"
|
|
996
|
+
headers: Authorization: Bearer <your_token>
|
|
976
997
|
|
|
977
998
|
// Request with body for Create Auth data
|
|
978
999
|
POST: "/authentication/create"
|
|
979
|
-
{
|
|
1000
|
+
payload: {
|
|
980
1001
|
username: "add_new_username",
|
|
981
1002
|
password: "add_new_secret",
|
|
982
1003
|
name: "add_new_my_name",
|
|
983
1004
|
email: "add_new_email"
|
|
984
1005
|
}
|
|
985
1006
|
|
|
986
|
-
|
|
987
1007
|
// Request with body for Update Auth data
|
|
988
1008
|
PATCH: "/authentication/update/1"
|
|
989
1009
|
headers: Authorization: Bearer <your_token>
|
|
990
|
-
{
|
|
1010
|
+
payload: {
|
|
991
1011
|
username: "update_bombkiml",
|
|
992
1012
|
password: "update_secret",
|
|
993
1013
|
name: "update_my_name",
|
|
@@ -1293,6 +1313,23 @@ module.exports = {
|
|
|
1293
1313
|
duplicateRequest: {
|
|
1294
1314
|
expiration: 500, // Can't duplicate request for 5 milliseconds each IP requests per `window`
|
|
1295
1315
|
},
|
|
1316
|
+
|
|
1317
|
+
payload: {
|
|
1318
|
+
// The limit of request body size, json default "100KB", urlencoded default "100KB". Learn more: https://expressjs.com/en/4x/api.html#express.json
|
|
1319
|
+
json: {
|
|
1320
|
+
limit: "100KB", // default: "100KB"
|
|
1321
|
+
},
|
|
1322
|
+
urlencoded: {
|
|
1323
|
+
limit: "100KB", // default: "100KB"
|
|
1324
|
+
extended: true, // default: true (reccomended: true)
|
|
1325
|
+
},
|
|
1326
|
+
file: {
|
|
1327
|
+
uploadAllowMethod: ["POST", "PATCH", "PUT"], // Only apply file upload limit for POST, PATCH, PUT method.
|
|
1328
|
+
allowedTypes: ["image/jpeg", "image/png", "application/pdf"], // Example: Allow only JPEG, PNG, and PDF files. Learn more: https://www.digipres.org/formats/mime-types/
|
|
1329
|
+
limit: 5 * 1024 * 1024, // 5MB (default: Infinity)
|
|
1330
|
+
},
|
|
1331
|
+
},
|
|
1332
|
+
|
|
1296
1333
|
},
|
|
1297
1334
|
},
|
|
1298
1335
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beech-api",
|
|
3
|
-
"version": "3.9.0-beta.
|
|
3
|
+
"version": "3.9.0-beta.9-rc",
|
|
4
4
|
"description": "Command line interface for rapid Beech API development",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"compression": "^1.7.5",
|
|
39
39
|
"cookie-parser": "^1.4.7",
|
|
40
40
|
"cors": "^2.8.1",
|
|
41
|
+
"cron": "^4.4.0",
|
|
41
42
|
"crypto-js": "^4.2.0",
|
|
42
43
|
"cryptr": "^6.3.0",
|
|
43
44
|
"express": "^4.20.0",
|
|
@@ -72,7 +73,7 @@
|
|
|
72
73
|
"walk": "^2.3.14"
|
|
73
74
|
},
|
|
74
75
|
"devDependencies": {
|
|
75
|
-
"jest": "^
|
|
76
|
+
"jest": "^30.2.0",
|
|
76
77
|
"nodemon": "^3.1.0",
|
|
77
78
|
"sequelize-cli": "^5.5.1"
|
|
78
79
|
},
|
|
@@ -231,7 +231,7 @@ class Beech {
|
|
|
231
231
|
successfully() {
|
|
232
232
|
clear();
|
|
233
233
|
console.log("[94mBeech CLI v" + require(__dirname + "/../../../package.json").version);
|
|
234
|
-
console.log('\n[102m[90m Passed [0m[0m The project has been successfully created.\n\n [37m$[0m [36mcd ' + this.argument + '[0m\n [37m$[0m [36mnpm
|
|
234
|
+
console.log('\n[102m[90m Passed [0m[0m The project has been successfully created.\n\n [37m$[0m [36mcd ' + this.argument + '[0m\n [37m$[0m [36mnpm run dev[0m or [36myarn dev[0m');
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
async contentReplace(pathFile, textCondition) {
|
|
@@ -48,13 +48,13 @@ class Beech {
|
|
|
48
48
|
setTimeout(() => {
|
|
49
49
|
clearInterval(refreshCompileIntervalId);
|
|
50
50
|
logUpdate("\n[36m[OK] Running...[0m");
|
|
51
|
-
},
|
|
51
|
+
}, 1500);
|
|
52
52
|
} else {
|
|
53
53
|
setTimeout(() => {
|
|
54
54
|
clearInterval(refreshCompileIntervalId);
|
|
55
55
|
logUpdate("\n[101m[ERR] Failed... [0m", err);
|
|
56
56
|
reject();
|
|
57
|
-
},
|
|
57
|
+
}, 1000);
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
}).on('error', (err) => {
|
|
@@ -91,6 +91,7 @@ class Beech {
|
|
|
91
91
|
const spawnData = new Promise((resolve) => {
|
|
92
92
|
// check Dev. run service
|
|
93
93
|
if(argument == "-D" || argument == "--dev") {
|
|
94
|
+
console.log("\n[101m Starting Beech service in Development mode [0m");
|
|
94
95
|
promise = this.spawn('npx', ['nodemon', '-q', './cli/beech']); // For Dev.
|
|
95
96
|
} else {
|
|
96
97
|
promise = this.spawn('npx', ['nodemon', '-q', './node_modules/beech-api/packages/cli/beech']); // For Prd.
|
|
@@ -37,74 +37,96 @@ const credentials = (req, res, next) => {
|
|
|
37
37
|
if (passport_config.jwt_allow === true) {
|
|
38
38
|
const auth_endpoint = (passport_config.auth_endpoint) ? (passport_config.auth_endpoint[ 0 ] === "/" ? passport_config.auth_endpoint : "/" + passport_config.auth_endpoint) : "/authentication";
|
|
39
39
|
const slashOneIsHash = auth_endpoint.split("/")[1];
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return
|
|
40
|
+
if(req.params === undefined) {
|
|
41
|
+
// Request is not valid
|
|
42
|
+
console.log("\n[101m REQUEST|OPTION [0m Error: Request is not valid, missing params.");
|
|
43
|
+
return;
|
|
44
44
|
} else {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
45
|
+
// Check first HASH equal Authentication
|
|
46
|
+
if(slashOneIsHash == req.params.hash) {
|
|
47
|
+
// Bypass authentication for auth endpoint
|
|
48
|
+
return next();
|
|
49
|
+
} else {
|
|
50
|
+
return passport.authenticate("jwt", {
|
|
51
|
+
session: false,
|
|
52
|
+
}, (err, user, info) => {
|
|
53
|
+
// error check
|
|
54
|
+
if (err) {
|
|
55
|
+
console.log(err, info);
|
|
56
|
+
return res.status(401).json({
|
|
57
|
+
code: 401,
|
|
58
|
+
error: "UNAUTHORIZED",
|
|
59
|
+
message: {
|
|
60
|
+
name: "WrongTokenError",
|
|
61
|
+
message: "token error.",
|
|
62
|
+
},
|
|
63
|
+
/* dev: { err, info }, */ // for dev info
|
|
64
|
+
});
|
|
65
|
+
} else {
|
|
66
|
+
// anything token check
|
|
67
|
+
if (!user) {
|
|
68
|
+
if (info) {
|
|
69
|
+
if (info.name == "TokenExpiredError") {
|
|
70
|
+
return res.status(401).json({
|
|
71
|
+
code: 401,
|
|
72
|
+
status: "TOKEN_EXPIRED",
|
|
73
|
+
message: info,
|
|
74
|
+
});
|
|
75
|
+
} else if (info.name == "Error") {
|
|
76
|
+
return res.status(401).json({
|
|
77
|
+
code: 401,
|
|
78
|
+
status: "NO_AUTH_TOKEN",
|
|
79
|
+
message: {
|
|
80
|
+
name: "NoTokenError",
|
|
81
|
+
message: "No auth token",
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
} else if (info.name == "SyntaxError") {
|
|
85
|
+
return res.status(401).json({
|
|
86
|
+
code: 401,
|
|
87
|
+
status: "PAYLOAD_SYNTAX_ERROR",
|
|
88
|
+
message: {
|
|
89
|
+
name: "SyntaxError",
|
|
90
|
+
message: "Unexpected token < in JSON at position 0",
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
} else if (info.name == "JsonWebTokenError") {
|
|
94
|
+
return res.status(401).json({
|
|
95
|
+
code: 401,
|
|
96
|
+
status: "INVALID_TOKEN",
|
|
97
|
+
message: {
|
|
98
|
+
name: "JsonWebTokenError",
|
|
99
|
+
message: "invalid token.",
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
} else {
|
|
103
|
+
return res.status(401).json({
|
|
104
|
+
code: 401,
|
|
105
|
+
status: "OTHER_TOKEN_ERR",
|
|
106
|
+
message: {
|
|
107
|
+
name: "OtherTokenError",
|
|
108
|
+
message: String(info),
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
82
113
|
return res.status(401).json({
|
|
83
114
|
code: 401,
|
|
84
|
-
status: "
|
|
85
|
-
message: {
|
|
86
|
-
name: "
|
|
87
|
-
message:
|
|
115
|
+
status: "UNAUTHORIZED_USER",
|
|
116
|
+
message: info || {
|
|
117
|
+
name: "TokenError",
|
|
118
|
+
message: String(info),
|
|
88
119
|
},
|
|
89
120
|
});
|
|
90
121
|
}
|
|
91
122
|
} else {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
status: "UNAUTHORIZED_USER",
|
|
95
|
-
message: info || {
|
|
96
|
-
name: "TokenError",
|
|
97
|
-
message: "Unauthorized token."
|
|
98
|
-
},
|
|
99
|
-
});
|
|
123
|
+
// Perfectly user jwt
|
|
124
|
+
return next();
|
|
100
125
|
}
|
|
101
|
-
} else {
|
|
102
|
-
// Perfectly user jwt
|
|
103
|
-
return next();
|
|
104
126
|
}
|
|
105
127
|
}
|
|
106
|
-
|
|
107
|
-
|
|
128
|
+
)(req, res, next);
|
|
129
|
+
}
|
|
108
130
|
}
|
|
109
131
|
} else {
|
|
110
132
|
// Bypass authentication
|
|
@@ -5,15 +5,14 @@
|
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"start": "beech-service serve",
|
|
8
|
+
"predev": "node ./node_modules/beech-api/packages/cli/core/test/check-node.js",
|
|
9
|
+
"prebuild": "node ./node_modules/beech-api/packages/cli/core/test/check-node.js",
|
|
10
|
+
"build": "set NODE_ENV=production&& beech-service build",
|
|
11
|
+
"dev": "beech-service serve",
|
|
13
12
|
"test": "node ./node_modules/jest/bin/jest __tests__ -o --watch --config"
|
|
14
13
|
},
|
|
15
14
|
"dependencies": {
|
|
16
|
-
"beech-api": "
|
|
15
|
+
"beech-api": "^3.9.0"
|
|
17
16
|
},
|
|
18
17
|
"devDependencies": {
|
|
19
18
|
"jest": "^29.7.0"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
const CronJob = require("cron").CronJob;
|
|
1
2
|
//const Users = require("@/models/Users");
|
|
2
|
-
//const CronJob = require("cron").CronJob;
|
|
3
3
|
|
|
4
4
|
exports.init = () => {
|
|
5
5
|
|
|
@@ -8,15 +8,25 @@ exports.init = () => {
|
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
//
|
|
11
|
+
// const getUsers = async () => {
|
|
12
|
+
// try {
|
|
13
|
+
// const users = await Users.findAll();
|
|
14
|
+
// console.log(users);
|
|
15
|
+
// } catch (error) {
|
|
16
|
+
// console.error(error);
|
|
17
|
+
// }
|
|
18
|
+
// };
|
|
12
19
|
|
|
13
20
|
/**
|
|
14
|
-
* Job schedule:
|
|
21
|
+
* Job schedule: seconds minutes hours dayOfMonth month dayOfWeek
|
|
15
22
|
* Learn more: https://github.com/kelektiv/node-cron
|
|
16
23
|
*
|
|
17
24
|
*/
|
|
18
|
-
|
|
19
|
-
//Run job.
|
|
20
|
-
|
|
25
|
+
new CronJob("0 0 0 * * *", () => {
|
|
26
|
+
// Run job.
|
|
27
|
+
|
|
28
|
+
// getUsers();
|
|
29
|
+
|
|
30
|
+
}, null, true, "Asia/Bangkok");
|
|
21
31
|
|
|
22
32
|
};
|
|
@@ -179,6 +179,18 @@ class Generator {
|
|
|
179
179
|
resolve("\n[101m Faltal [0m commnad it's not available.");
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
|
+
} else if (this.option == 'update') {
|
|
183
|
+
console.log('------------------------------');
|
|
184
|
+
if(this.argument == 'model') {
|
|
185
|
+
// this.special = model name
|
|
186
|
+
// if (!this.special) {
|
|
187
|
+
// resolve("\n [103m [90m Warning [0m [0m Please specify model name to update.");
|
|
188
|
+
// } else {
|
|
189
|
+
// this.updateModel()
|
|
190
|
+
// .then(res => resolve(res))
|
|
191
|
+
// .catch(err => reject(err));
|
|
192
|
+
// }
|
|
193
|
+
}
|
|
182
194
|
} else if (this.option == 'passport') {
|
|
183
195
|
if (this.argument == "init") {
|
|
184
196
|
this.makePassportInit()
|
|
@@ -451,6 +463,74 @@ class Generator {
|
|
|
451
463
|
});
|
|
452
464
|
}
|
|
453
465
|
|
|
466
|
+
updateModel() {
|
|
467
|
+
return new Promise((resolve, reject) => {
|
|
468
|
+
try {
|
|
469
|
+
// 1. อ่านการตั้งค่าจาก global.config และ app.config เหมือนตอนสร้าง Model
|
|
470
|
+
this.fs.readFile("./global.config.js", 'utf8', (err, globalData) => {
|
|
471
|
+
if (err) return resolve("\n [101m Faltal [0m Can't read `global.config.js` file.");
|
|
472
|
+
|
|
473
|
+
this.fs.readFile("./app.config.js", 'utf8', (appErr, appData) => {
|
|
474
|
+
if (appErr) return resolve("\n [101m Faltal [0m Can't read `app.config.js` file.");
|
|
475
|
+
|
|
476
|
+
let appBuf2eval = eval(appData);
|
|
477
|
+
let modelName = this.special; // <table_name>
|
|
478
|
+
let modelPath = `./src/models/${modelName.charAt(0).toUpperCase() + modelName.slice(1)}.js`;
|
|
479
|
+
|
|
480
|
+
// ตรวจสอบว่ามีไฟล์ Model เดิมอยู่หรือไม่
|
|
481
|
+
if (!this.fs.existsSync(modelPath)) {
|
|
482
|
+
return resolve(`\n [103m [90m Warning [0m [0m Model file \`${modelName}\` not found to update.`);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// ให้ผู้ใช้เลือก Connection ที่ต้องการดึง Schema ใหม่
|
|
486
|
+
inquirer.prompt([{
|
|
487
|
+
type: "list",
|
|
488
|
+
name: "selectDbConnect",
|
|
489
|
+
message: " [93mSelect database connection to sync schema: [0m",
|
|
490
|
+
choices: appBuf2eval.database_config.map(e => e.name),
|
|
491
|
+
}]).then(dbSelected => {
|
|
492
|
+
|
|
493
|
+
// 2. ดึง Schema ใหม่จาก DB
|
|
494
|
+
const { connectForGenerateModel } = require("../databases/test");
|
|
495
|
+
let newModelClassName = modelName.split("_").map(e => e.charAt(0).toUpperCase() + e.slice(1)).join("");
|
|
496
|
+
|
|
497
|
+
connectForGenerateModel(dbSelected.selectDbConnect, modelName, appBuf2eval.database_config, (err, tableSchema, tableName) => {
|
|
498
|
+
if (err) return reject(err);
|
|
499
|
+
|
|
500
|
+
// 3. แปลง Schema เป็น Code String
|
|
501
|
+
this.rawSchemaTable(dbSelected.selectDbConnect, newModelClassName, tableName, tableSchema, (SchemaErr, rawSchema) => {
|
|
502
|
+
if (SchemaErr) return reject(SchemaErr);
|
|
503
|
+
|
|
504
|
+
// 4. อ่านไฟล์เดิมและทำการ Replace เฉพาะส่วน Schema
|
|
505
|
+
this.fs.readFile(modelPath, 'utf8', (readErr, currentContent) => {
|
|
506
|
+
if (readErr) return reject(readErr);
|
|
507
|
+
|
|
508
|
+
// ใช้ Regex เพื่อหาช่วงของ Schema(...).define(...) และเปลี่ยนเฉพาะข้างใน
|
|
509
|
+
// โดยจะหาตั้งแต่ Schema(sql.db).define("table", { จนถึง });
|
|
510
|
+
const regex = /const\s+\w+\s+=\s+Schema\(sql\.\w+\)\.define\([\s\S]*?\}\);/;
|
|
511
|
+
|
|
512
|
+
if (regex.test(currentContent)) {
|
|
513
|
+
let updatedContent = currentContent.replace(regex, rawSchema);
|
|
514
|
+
|
|
515
|
+
this.fs.writeFile(modelPath, updatedContent, 'utf8', (writeErr) => {
|
|
516
|
+
if (writeErr) return reject(writeErr);
|
|
517
|
+
resolve(`\n [102m [90m Passed [0m [0m The model \`${modelName}\` schema has been updated.`);
|
|
518
|
+
});
|
|
519
|
+
} else {
|
|
520
|
+
resolve("\n [101m Faltal [0m Could not find Schema definition pattern in the file.");
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
} catch (error) {
|
|
529
|
+
reject(error);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
|
|
454
534
|
generateModel(tmpModelsPath, dbSelected, appBuf2eval, pool_base) {
|
|
455
535
|
return new Promise((resolve, reject) => {
|
|
456
536
|
try {
|