beech-api 3.9.0-beta.8-rc → 3.9.57

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 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](#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](#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/hySFxy3/install-beech720-1.gif)
84
- ![Alt Text](https://i.ibb.co/hySFxy3/install-beech720-1.gif)
83
+ [Demo 1](https://i.ibb.co/TBjNgVrF/1-11.gif)
84
+ ![Alt Text](https://i.ibb.co/TBjNgVrF/1-11.gif)
85
+
86
+ [Demo 2](https://i.ibb.co/wrdgyzDP/ezgif-8539024298ec62a9.gif)
87
+ ![Alt Text](https://i.ibb.co/wrdgyzDP/ezgif-8539024298ec62a9.gif)
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:
@@ -143,7 +146,15 @@ The following commands are available:
143
146
  $ beech make <endpoint> Create a new Endpoints and unit test file,
144
147
  You might using [special] `-R, --require`
145
148
  for choose Model(s) used to endpoint file.
146
- $ beech make <model> -M, --model Create a new Models file.
149
+
150
+ $ beech make <model> -M, --model Create a new Models file, You might using
151
+ [special] `--no-comment` for ignore comment
152
+ Table Property in your Schema.
153
+
154
+ $ beech update model <model_name> Update new Table Structure for latest, You
155
+ might using [special] `--no-comment` for
156
+ ignore comment Table Property in your Schema.
157
+
147
158
  $ beech make <helper> --helper Create a new Helpers file.
148
159
  $ beech passport init Initialize authentication with passport-jwt.
149
160
  $ beech skd init Initialize Job Scheduler file.
@@ -179,7 +190,7 @@ Output: FjgcgJPylkV7EeQJjea_EeifPwaHVO9onD3ATk3YYAyvjtMGu3dcDS0ejA
179
190
 
180
191
  📂 app.config.js
181
192
  ```js
182
- // basic & sequelize (needed Hash)
193
+ // Database configuration Only Basic & Sequelize (needed Hash)
183
194
 
184
195
  ...
185
196
 
@@ -217,6 +228,8 @@ database_config: [
217
228
  ```
218
229
  ❓ **Caution! :** Every re-new generate `app_key`. Must to new Hash your Access and change to ALL Database connections.
219
230
 
231
+ ❓ **Development** : You can add ```dev: true``` in ```main_config``` for a better experience.
232
+
220
233
  # Models
221
234
 
222
235
  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 +338,7 @@ Fruit.options = {
325
338
  // Option 2: Allow with specific per methods
326
339
  defaultEndpoint: {
327
340
  GET: true,
328
- POST: false,
329
- PATCH: {
341
+ POST: {
330
342
  allow: true, // allow Auto-Endpoint
331
343
  jwt: {
332
344
  allow: true, // allow JWT
@@ -334,7 +346,19 @@ Fruit.options = {
334
346
  { role: [1, 2] },
335
347
  ],
336
348
  },
349
+ // Rate Limit for POST
350
+ rate_limit: {
351
+ windowMs: 15 * 60 * 1000, // 15 minutes
352
+ limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
353
+ // store: ... , // Redis, Memcached, etc.
354
+ // See more: https://www.npmjs.com/package/express-rate-limit#Configuration
355
+ },
356
+ // Duplicate Request for POST
357
+ duplicate_request: {
358
+ expiration: 500, // Can't duplicate request for 5 milliseconds each IP requests per `window` (Disabled for Set expiration to 0 zero.)
359
+ },
337
360
  },
361
+ PATCH: false,
338
362
  DELETE: false,
339
363
  },
340
364
 
@@ -379,16 +403,17 @@ Now you can add Query String with Conditional, Grouping and Ordering (Now Suppor
379
403
 
380
404
  <b style="font-size:12pt">For Example, Now!</b>, You can request to `/fruit` with methods GET, POST, PATCH and DELETE like this.
381
405
 
382
- | Efficacy | Method | Endpoint | Body |
383
- |:---------|:---------|:-----------------------|:-----------|
384
- | Create | POST | /fruit | { } |
385
- | Read | GET | /fruit | No |
386
- | Read | GET | /fruit/:limit/:offset | No |
387
- | Read | GET | /fruit?someField=1 | No |
388
- | Read | GET | /fruit?orderby=sort | No |
389
- | Read | GET | /fruit?groupby=id | No |
390
- | Update | PATCH | /fruit/:id | { } |
391
- | Delete | DELETE | /fruit/:id | No |
406
+ | Efficacy | Method | Endpoint | Body | Mode |
407
+ |:---------|:---------|:-----------------------|:-------------|:--------|
408
+ | Create | POST | /fruit | { } | Single |
409
+ | Create | POST | /fruit | [ ] | Bulk |
410
+ | Read | GET | /fruit | No | Fetch |
411
+ | Read | GET | /fruit/:limit/:offset | No | Fetch |
412
+ | Read | GET | /fruit?someField=1 | No | Fetch |
413
+ | Read | GET | /fruit?orderby=sort | No | Fetch |
414
+ | Read | GET | /fruit?groupby=id | No | Fetch |
415
+ | Update | PATCH | /fruit/:id | { } | Single |
416
+ | Delete | DELETE | /fruit/:id | No | Single |
392
417
 
393
418
  Add some Basic Conditions, Grouping and Ordering with `QUERY STRING` under GET methods<br/>
394
419
 
@@ -627,15 +652,15 @@ The **JWT Broken Role** mechanism provides a flexible way to bypass or override
627
652
 
628
653
  #### It can be applied in three different levels, depending on your use case.
629
654
 
630
- | Level | Scope | Best For |
631
- |:--------------------------------|:------------------------|:--------------------------------|
632
- | Global (``passport.config.js``) | All endpoints | Common authorization exceptions |
633
- | Model-level options | Per model & HTTP method | Structured, reusable rules |
634
- | Endpoint middleware | Single endpoint | Custom or special cases |
655
+ | Priority | Level | Scope | Best For |
656
+ |:---------|:--------------------------------|:-------------------------|:--------------------------------|
657
+ | 3rd | Global (``passport.config.js``) | All endpoints | Common authorization exceptions |
658
+ | 2nd | Model-level options | Per model & HTTP method | Structured, reusable rules |
659
+ | 1st | Endpoint middleware | Single endpoint | Custom or special cases |
635
660
 
636
661
  This multi-layer approach allows you to design **secure, flexible, and maintainable JWT authorization flows**.
637
662
 
638
- ### 1. Global Configuration (passport.config.js)
663
+ ### 1. Global Configuration (passport.config.js) - Priority 3
639
664
 
640
665
  You can define global JWT Broken Role rules that apply to all endpoints by configuring them in ``passport.config.js``.
641
666
  ```js
@@ -667,7 +692,7 @@ module.exports = {
667
692
  - Apply common authorization exceptions across the entire application
668
693
  - Support both simple role arrays and advanced operators (``$in``, ``$regex``, etc.)
669
694
 
670
- ### 2. Model-Level Configuration (Per HTTP Method)
695
+ ### 2. Model-Level Configuration (Per HTTP Method) - Priority 2
671
696
 
672
697
  You can configure JWT and Broken Role rules per model and per HTTP method using model options.
673
698
 
@@ -714,7 +739,7 @@ Fruit.options = {
714
739
  - Different role requirements for POST, PATCH, etc.
715
740
  - Combine role-based and attribute-based conditions
716
741
 
717
- ### 3. Endpoint-Level Configuration (Credentials Middleware)
742
+ ### 3. Endpoint-Level Configuration (Credentials Middleware) - Priority 1
718
743
 
719
744
  For maximum flexibility, you can define Broken Role rules directly on a specific endpoint using the Credentials middleware.
720
745
 
@@ -782,8 +807,8 @@ Credentials([
782
807
  // Common Matching Patterns
783
808
  { role: [1, 2, 3] } // IN
784
809
  { role: 1 } // Equal
785
- { email: 'a@b.com' }
786
- { department: 'IT' }
810
+ { email: 'a@b.com' } // Equal
811
+ { department: 'IT' } // Equal
787
812
  { status: ['active'] } // Dynamic key
788
813
  ```
789
814
 
@@ -959,8 +984,9 @@ module.exports = {
959
984
  When you config passport with ```users``` table already. You will got Auth endpoint in available.
960
985
  ```js
961
986
  POST: "/authentication" // Request token
987
+ POST: "/authentication/refresh" // Request refresh token
962
988
  POST: "/authentication/create" // Create new Auth data
963
- PATCH: "/authentication/update/:id" // Update old Auth data (needed id)
989
+ PATCH: "/authentication/update/:id" // Update old Auth data (needed user id)
964
990
  ```
965
991
 
966
992
  ***XHR Example :***
@@ -968,26 +994,28 @@ PATCH: "/authentication/update/:id" // Update old Auth data (needed id)
968
994
  ```js
969
995
  // Request with body for gether Token
970
996
  POST: "/authentication"
971
- {
997
+ payload: {
972
998
  username: "bombkiml",
973
999
  password: "secret"
974
1000
  }
975
1001
 
1002
+ // Request with header for refresh token
1003
+ POST: "/authentication/refresh"
1004
+ headers: Authorization: Bearer <your_token>
976
1005
 
977
1006
  // Request with body for Create Auth data
978
1007
  POST: "/authentication/create"
979
- {
1008
+ payload: {
980
1009
  username: "add_new_username",
981
1010
  password: "add_new_secret",
982
1011
  name: "add_new_my_name",
983
1012
  email: "add_new_email"
984
1013
  }
985
1014
 
986
-
987
1015
  // Request with body for Update Auth data
988
1016
  PATCH: "/authentication/update/1"
989
1017
  headers: Authorization: Bearer <your_token>
990
- {
1018
+ payload: {
991
1019
  username: "update_bombkiml",
992
1020
  password: "update_secret",
993
1021
  name: "update_my_name",
@@ -1293,6 +1321,23 @@ module.exports = {
1293
1321
  duplicateRequest: {
1294
1322
  expiration: 500, // Can't duplicate request for 5 milliseconds each IP requests per `window`
1295
1323
  },
1324
+
1325
+ payload: {
1326
+ // The limit of request body size, json default "100KB", urlencoded default "100KB". Learn more: https://expressjs.com/en/4x/api.html#express.json
1327
+ json: {
1328
+ limit: "100KB", // default: "100KB"
1329
+ },
1330
+ urlencoded: {
1331
+ limit: "100KB", // default: "100KB"
1332
+ extended: true, // default: true (reccomended: true)
1333
+ },
1334
+ file: {
1335
+ uploadAllowMethod: ["POST", "PATCH", "PUT"], // Only apply file upload limit for POST, PATCH, PUT method.
1336
+ allowedTypes: ["image/jpeg", "image/png", "application/pdf"], // Example: Allow only JPEG, PNG, and PDF files. Learn more: https://www.digipres.org/formats/mime-types/
1337
+ limit: 5 * 1024 * 1024, // 5MB (default: Infinity)
1338
+ },
1339
+ },
1340
+
1296
1341
  },
1297
1342
  },
1298
1343
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beech-api",
3
- "version": "3.9.0-beta.8-rc",
3
+ "version": "3.9.57",
4
4
  "description": "Command line interface for rapid Beech API development",
5
5
  "keywords": [
6
6
  "api",
@@ -32,12 +32,14 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "app-root-path": "^3.0.0",
35
+ "ast-types": "^0.14.2",
35
36
  "axios": "^1.8.4",
36
37
  "child-process-promise": "^2.2.1",
37
38
  "cli-clear": "^1.0.4",
38
39
  "compression": "^1.7.5",
39
40
  "cookie-parser": "^1.4.7",
40
41
  "cors": "^2.8.1",
42
+ "cron": "^4.4.0",
41
43
  "crypto-js": "^4.2.0",
42
44
  "cryptr": "^6.3.0",
43
45
  "express": "^4.20.0",
@@ -55,6 +57,7 @@
55
57
  "method-override": "^3.0.0",
56
58
  "mkdirp": "^2.1.6",
57
59
  "module-alias": "^2.2.2",
60
+ "multer": "^2.1.1",
58
61
  "mysql": "^2.18.1",
59
62
  "mysql2": "^3.14.0",
60
63
  "node-cmd": "^3.0.0",
@@ -67,12 +70,13 @@
67
70
  "passport-local": "^1.0.0",
68
71
  "passport-oauth": "^1.0.0",
69
72
  "path": "^0.12.7",
73
+ "recast": "^0.23.11",
70
74
  "sequelize": "^6.21.3",
71
75
  "tedious": "^18.6.1",
72
76
  "walk": "^2.3.14"
73
77
  },
74
78
  "devDependencies": {
75
- "jest": "^29.7.0",
79
+ "jest": "^30.2.0",
76
80
  "nodemon": "^3.1.0",
77
81
  "sequelize-cli": "^5.5.1"
78
82
  },
@@ -231,7 +231,7 @@ class Beech {
231
231
  successfully() {
232
232
  clear();
233
233
  console.log("Beech CLI v" + require(__dirname + "/../../../package.json").version);
234
- console.log('\n Passed  The project has been successfully created.\n\n $ cd ' + this.argument + '\n $ npm start or yarn start');
234
+ console.log('\n Passed  The project has been successfully created.\n\n $ cd ' + this.argument + '\n $ npm run dev or yarn dev');
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[OK] Running...");
51
- }, 2500);
51
+ }, 1500);
52
52
  } else {
53
53
  setTimeout(() => {
54
54
  clearInterval(refreshCompileIntervalId);
55
55
  logUpdate("\n[ERR] Failed... ", err);
56
56
  reject();
57
- }, 2500);
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 Starting Beech service in Development mode ");
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
- // Check first HASH equal Authentication
41
- if(slashOneIsHash == req.params.hash) {
42
- // Bypass authentication for auth endpoint
43
- return next();
40
+ if(req.params === undefined) {
41
+ // Request is not valid
42
+ console.log("\n REQUEST|OPTION  Error: Request is not valid, missing params.");
43
+ return;
44
44
  } else {
45
- return passport.authenticate("jwt", {
46
- session: false,
47
- }, (err, user, info) => {
48
- // error check
49
- if (err) {
50
- console.log(err, info);
51
- return res.status(401).json({
52
- code: 401,
53
- error: "UNAUTHORIZED",
54
- message: {
55
- name: "WrongTokenError",
56
- message: "token error.",
57
- },
58
- /* dev: { err, info }, */ // for dev info
59
- });
60
- } else {
61
- // anything token check
62
- if (!user) {
63
- if (info) {
64
- if (info.name == "TokenExpiredError") {
65
- return res.status(401).json({
66
- code: 401,
67
- status: "TOKEN_EXPIRED",
68
- message: info,
69
- });
70
- }
71
- if (info.name == "Error") {
72
- return res.status(401).json({
73
- code: 401,
74
- status: "NO_AUTH_TOKEN",
75
- message: {
76
- name: "NoTokenError",
77
- message: "No auth token",
78
- },
79
- });
80
- }
81
- if (info.name == "SyntaxError") {
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: "PAYLOAD_SYNTAX_ERROR",
85
- message: {
86
- name: "SyntaxError",
87
- message: "Unexpected token < in JSON at position 0",
115
+ status: "UNAUTHORIZED_USER",
116
+ message: info || {
117
+ name: "TokenError",
118
+ message: String(info),
88
119
  },
89
120
  });
90
121
  }
91
122
  } else {
92
- return res.status(401).json({
93
- code: 401,
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
- )(req, res, next);
128
+ )(req, res, next);
129
+ }
108
130
  }
109
131
  } else {
110
132
  // Bypass authentication
@@ -18,7 +18,7 @@ function filterDbIsTrue(dbConfig, cb) {
18
18
  }
19
19
  setTimeout(() => {
20
20
  cb(null, dbIsTrue);
21
- }, 300);
21
+ }, 200);
22
22
  }
23
23
  });
24
24
  } catch (error) {
@@ -11,7 +11,15 @@ The following commands are available:
11
11
  $ beech make <endpoint> Create a new Endpoints and unit test file,
12
12
  You might using [special] `-R, --require`
13
13
  for choose Model(s) used to endpoint file.
14
- $ beech make <model> -M, --model Create a new Models file.
14
+
15
+ $ beech make <model> -M, --model Create a new Models file, You might using
16
+ [special] `--no-comment` for ignore comment
17
+ Table Property in your Schema.
18
+
19
+ $ beech update model <model_name> Update new Table Structure for latest, You
20
+ might using [special] `--no-comment` for
21
+ ignore comment Table Property in your Schema.
22
+
15
23
  $ beech make <helper> --helper Create a new Helpers file.
16
24
  $ beech passport init Initialize authentication with passport-jwt.
17
25
  $ beech skd init Initialize Job Scheduler file.
@@ -5,15 +5,14 @@
5
5
  "license": "MIT",
6
6
  "main": "index.js",
7
7
  "scripts": {
8
- "prod": "set NODE_ENV=production&& npm start",
9
- "production": "set NODE_ENV=production&& npm start",
10
- "dev": "set NODE_ENV=development&& npm start",
11
- "development": "set NODE_ENV=development&& npm start",
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": "~3.9.0"
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
- //Users.findAll().then(console.log)
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: seccod minute hour dayOfMonth month dayOfWeek
21
+ * Job schedule: seconds minutes hours dayOfMonth month dayOfWeek
15
22
  * Learn more: https://github.com/kelektiv/node-cron
16
23
  *
17
24
  */
18
- //new CronJob("0 0 0 * * *", () => {
19
- //Run job.
20
- //}, null, true, "Asia/Bangkok");
25
+ new CronJob("0 0 0 * * *", () => {
26
+ // Run job.
27
+
28
+ // getUsers();
29
+
30
+ }, null, true, "Asia/Bangkok");
21
31
 
22
32
  };