beech-api 3.8.0 → 3.9.0-beta.8-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 +498 -130
- package/package.json +12 -12
- package/packages/cli/bin/beech-app.js +3 -3
- package/packages/cli/bin/beech-service.js +61 -25
- package/packages/cli/core/auth/Credentials.js +92 -87
- package/packages/cli/core/auth/Passport.js +111 -39
- package/packages/cli/core/configure/passport.config.js +1 -1
- package/packages/cli/core/databases/mysql.js +1 -1
- package/packages/cli/core/databases/sequelize.js +15 -6
- package/packages/cli/core/databases/test.js +124 -38
- package/packages/cli/core/generator/_models +3 -26
- package/packages/cli/core/generator/_models_basic +0 -9
- package/packages/cli/core/generator/_package +2 -2
- package/packages/cli/core/generator/index.js +197 -23
- package/packages/cli/core/helpers/2fa.js +22 -1
- package/packages/cli/core/helpers/math.js +14 -2
- package/packages/cli/core/helpers/poolEntity.js +70 -26
- package/packages/cli/core/index.js +8 -3
- package/packages/cli/core/middleware/express/jwtCheckAllow.js +52 -34
- package/packages/cli/core/middleware/origin/guard/advance.js +5 -4
- package/packages/cli/core/services/http.express.js +0 -2
- package/packages/lib/src/endpoint.js +306 -103
- package/packages/lib/src/schema.js +4 -1
package/README.md
CHANGED
|
@@ -1,22 +1,74 @@
|
|
|
1
1
|
[](https://github.com/bombkiml)
|
|
2
2
|
|
|
3
3
|
# Beech API framework
|
|
4
|
-
####
|
|
4
|
+
#### Auto endpoint v.3.9.0 (LTS)
|
|
5
5
|
|
|
6
|
-
[](https://github.com/bombkiml/beech-api/releases/
|
|
6
|
+
[](https://github.com/bombkiml/beech-api/releases/)
|
|
7
7
|
[](https://github.com/bombkiml/beech-api/blob/master/README.md)
|
|
8
8
|
|
|
9
9
|
# What is Beech API ?
|
|
10
10
|
|
|
11
11
|
The Beech API is API framework, It's help you with very easy to create API project under [Node.js](https://nodejs.org)
|
|
12
12
|
|
|
13
|
+
## Let's go
|
|
14
|
+
- <b>[Installation](#installation)</b>
|
|
15
|
+
- <b>[Creating a project](#creating-a-project)</b>
|
|
16
|
+
- <b>[Upgrade to latest version](#upgrade-to-latest-version)</b>
|
|
17
|
+
- <b>[Beech CLI tool available](#beech-cli-tool-available)</b>
|
|
18
|
+
- ✨ <b>Automation Endpoints with CRUD</b>
|
|
19
|
+
- [Database Connection](#database-connection)
|
|
20
|
+
- [Model](#model)
|
|
21
|
+
- [Retrieving data with Query String](#retrieving-data-with-query-string)
|
|
22
|
+
- Conditions
|
|
23
|
+
- Grouping
|
|
24
|
+
- Ordering
|
|
25
|
+
- Timestamps (Add-on in Store and Update)
|
|
26
|
+
- [Transactions](#transactions)
|
|
27
|
+
- [Disorganized transactions](#way-1---disorganized-transactions-)
|
|
28
|
+
- [Organized transactions](#way-2---organized-transactions-)
|
|
29
|
+
- [Transactions set Isolation levels](#way-3---transactions-set-isolation-levels-)
|
|
30
|
+
- [Endpoints](#endpoints)
|
|
31
|
+
- [Helpers](#helpers)
|
|
32
|
+
- 🔐 <b>System Management of Authentication</b>
|
|
33
|
+
- [Authentication Manegement](#authentication-passport-jwt)
|
|
34
|
+
- [Request Token](#request-token)
|
|
35
|
+
- [Beech Guard](#beech-guard)
|
|
36
|
+
- Verify Identity Management
|
|
37
|
+
- Two Factor (2fa, OTP, etc.)
|
|
38
|
+
- [Beech User Authentication Managements](#-beech-user-authentication-managements)
|
|
39
|
+
- Create Auth
|
|
40
|
+
- Update Auth
|
|
41
|
+
- 🛠️ <b>Safe Endpoints Request</b>
|
|
42
|
+
- [Rate Limit](#-custom-endpoint-specific-rate-limit)
|
|
43
|
+
- [Slow Down](#-custom-endpoint-specific-slow-down)
|
|
44
|
+
- [Block Duplicate Request per Window](#-custom-endpoint-specific-duplicate-request)
|
|
45
|
+
- [JWT Broken Role](#jwt-broken-role)
|
|
46
|
+
- [Advance Guard (Timimg)](#-beech-advanced-guard-timing)
|
|
47
|
+
- 🙂 <b>Hight Security under passport-jwt, oauth2</b>
|
|
48
|
+
- 🌐 <b>Supported Official Strategy</b>
|
|
49
|
+
- [Google](#-google-strategy)
|
|
50
|
+
- [Facebook](#-facebook-strategy)
|
|
51
|
+
- 🖥️ <b>CORS Origin & Server Configuration</b>
|
|
52
|
+
- [Config Base public path `./`](#cors-origin--server-configuration)
|
|
53
|
+
- [Allow origin whitelist](#cors-origin--server-configuration)
|
|
54
|
+
- 📚 <b>Databases Managements</b>
|
|
55
|
+
- [Migrations](#databases-managements)
|
|
56
|
+
- [Seeder](#-creating-first-seeder)
|
|
57
|
+
- ☕ <b>Testing</b>
|
|
58
|
+
- [Jest](#testing)
|
|
59
|
+
- 🏃 <b>Implementration</b>
|
|
60
|
+
- [Docker](#implementation)
|
|
61
|
+
- [PM2](#-implement-with-pm2)
|
|
62
|
+
|
|
13
63
|
# Environment
|
|
14
64
|
|
|
15
|
-
- [`Node.js`](https://nodejs.org) >=
|
|
65
|
+
- [`Node.js`](https://nodejs.org) >= 18.17.1+ (recommended)
|
|
16
66
|
|
|
17
67
|
# Installation
|
|
18
68
|
|
|
19
|
-
Beech API
|
|
69
|
+
Beech API needed Node.js version 18.17.1 or above. You can management multiple versions on the same machine with [nvm](https://github.com/creationix/nvm) or [nvm-windows](https://github.com/coreybutler/nvm-windows).
|
|
70
|
+
|
|
71
|
+
<b>So, Let's go to install</b> `beech-api`
|
|
20
72
|
|
|
21
73
|
```sh
|
|
22
74
|
# NPM
|
|
@@ -98,7 +150,9 @@ The following commands are available:
|
|
|
98
150
|
$ beech key:generate, key:gen Re-Generate application key (Dangerous!).
|
|
99
151
|
$ beech hash:<text> Hash text for Access to Database connection.
|
|
100
152
|
```
|
|
101
|
-
❓ **Note:** Every to create new project will be generated new ``app_key`` in ``app.config.js`` file
|
|
153
|
+
❓ **Note:** Every to create new project will be generated new ``app_key`` in ``app.config.js`` file.
|
|
154
|
+
|
|
155
|
+
❓ **Note:** If you can re-generate. Can use command ``$ beech key:generate`` or ``$ beech key:gen``
|
|
102
156
|
|
|
103
157
|
# Database connection
|
|
104
158
|
|
|
@@ -121,7 +175,7 @@ $ beech hash:password
|
|
|
121
175
|
Output: FjgcgJPylkV7EeQJjea_EeifPwaHVO9onD3ATk3YYAyvjtMGu3dcDS0ejA
|
|
122
176
|
|
|
123
177
|
```
|
|
124
|
-
Example
|
|
178
|
+
***For Example :***
|
|
125
179
|
|
|
126
180
|
📂 app.config.js
|
|
127
181
|
```js
|
|
@@ -150,9 +204,9 @@ database_config: [
|
|
|
150
204
|
// or use a named timezone supported by Intl.Locale
|
|
151
205
|
// timezone: 'America/Los_Angeles',
|
|
152
206
|
|
|
153
|
-
logging: console.log, // SQL trace logs
|
|
207
|
+
logging: console.log, // SQL trace logs. Learn more: https://sequelize.org/docs/v6/getting-started/#logging
|
|
154
208
|
|
|
155
|
-
is_connect: true, //
|
|
209
|
+
is_connect: true, // Boolean, Turn ON/OFF to connect
|
|
156
210
|
},
|
|
157
211
|
|
|
158
212
|
...
|
|
@@ -163,113 +217,24 @@ database_config: [
|
|
|
163
217
|
```
|
|
164
218
|
❓ **Caution! :** Every re-new generate `app_key`. Must to new Hash your Access and change to ALL Database connections.
|
|
165
219
|
|
|
166
|
-
#
|
|
220
|
+
# Models
|
|
167
221
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
The `endpoints` keep the endpoints basic request files currently support `GET`, `POST`, `PUT`, `PATCH` and `DELETE`.
|
|
171
|
-
|
|
172
|
-
So, you might create new endpoints with constant `endpoint` object variable in `src/endpoints/` folder and file neme must be end with `-endpoints.js`
|
|
222
|
+
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.
|
|
173
223
|
|
|
174
224
|
```sh
|
|
175
|
-
$ beech make
|
|
176
|
-
```
|
|
177
|
-
You might using [special] `-R, --require` for choose Model(s) used for that endpoint.
|
|
178
|
-
|
|
179
|
-
### Example ***(Basic)*** : Fruit endpoints.
|
|
180
|
-
|
|
181
|
-
📂 fruit-endpoints.js
|
|
182
|
-
```js
|
|
183
|
-
exports.init = () => {
|
|
184
|
-
|
|
185
|
-
// GET method
|
|
186
|
-
endpoint.get("/fruit", Credentials, (req, res) => {
|
|
187
|
-
// @response
|
|
188
|
-
res.json({
|
|
189
|
-
code: 200,
|
|
190
|
-
status: "SUCCESS",
|
|
191
|
-
message: "GET /fruit request.",
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// POST method
|
|
197
|
-
endpoint.post("/fruit", Credentials, (req, res) => {
|
|
198
|
-
// @response
|
|
199
|
-
res.json({
|
|
200
|
-
code: 200,
|
|
201
|
-
status: "SUCCESS",
|
|
202
|
-
message: "POST request at /fruit",
|
|
203
|
-
result: {
|
|
204
|
-
id: req.body.id,
|
|
205
|
-
name: req.body.name,
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// PUT method
|
|
212
|
-
endpoint.put("/fruit/:id", Credentials, (req, res) => {
|
|
213
|
-
// @response
|
|
214
|
-
res.json({
|
|
215
|
-
code: 200,
|
|
216
|
-
status: "SUCCESS",
|
|
217
|
-
message: "PUT request at /fruit/" + req.params.id,
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// DELETE method
|
|
223
|
-
endpoint.delete("/fruit/:id", Credentials, (req, res) => {
|
|
224
|
-
// @response
|
|
225
|
-
res.json({
|
|
226
|
-
code: 200,
|
|
227
|
-
status: "SUCCESS",
|
|
228
|
-
message: "DELETE request at /fruit/" + req.params.id,
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
...
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Example ***(Sequelize)*** : Fruit endpoints.
|
|
238
|
-
|
|
239
|
-
📂 fruit-endpoints.js
|
|
240
|
-
```js
|
|
241
|
-
// Require Model schema, Function & Others
|
|
242
|
-
const { Fruit } = require("@/models/Fruit");
|
|
243
|
-
|
|
244
|
-
exports.init = () => {
|
|
245
|
-
|
|
246
|
-
// GET method
|
|
247
|
-
endpoint.get('/fruit', async (req, res) => {
|
|
248
|
-
// example call Fruit model for get data
|
|
249
|
-
res.json({
|
|
250
|
-
code: 200,
|
|
251
|
-
status: "SUCCESS",
|
|
252
|
-
results: await Fruit.findAll();
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
...
|
|
257
|
-
|
|
258
|
-
}
|
|
225
|
+
$ beech make modelName --model
|
|
259
226
|
```
|
|
260
227
|
|
|
228
|
+
## # Model (Basic)
|
|
261
229
|
|
|
262
|
-
|
|
230
|
+
Basic model only support `MySQL` Raw Query format and freedom of your SQL query
|
|
263
231
|
|
|
264
|
-
The
|
|
232
|
+
❓ **Note:** The Basic pool engine it's not support auto Endpoints.
|
|
265
233
|
|
|
266
|
-
```sh
|
|
267
|
-
$ beech make modelName --model
|
|
268
|
-
```
|
|
269
234
|
|
|
270
|
-
|
|
235
|
+
***For example :***
|
|
271
236
|
|
|
272
|
-
📂 Fruit.js
|
|
237
|
+
📂 models/Fruit.js
|
|
273
238
|
```js
|
|
274
239
|
module.exports = {
|
|
275
240
|
|
|
@@ -299,11 +264,17 @@ module.exports = {
|
|
|
299
264
|
};
|
|
300
265
|
```
|
|
301
266
|
|
|
302
|
-
|
|
267
|
+
## # Model (Sequelize)
|
|
303
268
|
|
|
269
|
+
Sequelize is a promise-based Node.js ORM tool for Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server, Oracle Database, Amazon Redshift and Snowflake’s Data Cloud. It features solid transaction support, relations, eager and lazy loading, read replication and more. <br/>You can learn more: [Sequelize docs](https://sequelize.org/docs/v6)
|
|
270
|
+
|
|
304
271
|
You can asign more DataTypes, Learn more : [Sequelize docs](https://sequelize.org/docs/v6/core-concepts/model-basics/#data-types)
|
|
305
272
|
|
|
306
|
-
|
|
273
|
+
❓ **Note:** When you generate a model it's create table structure for automatically for you.
|
|
274
|
+
|
|
275
|
+
***For example :***
|
|
276
|
+
|
|
277
|
+
📂 models/Fruit.js
|
|
307
278
|
```js
|
|
308
279
|
const { Schema } = require("beech-api");
|
|
309
280
|
|
|
@@ -327,6 +298,7 @@ const Fruit = Schema(sql.default_db).define("fruit", {
|
|
|
327
298
|
type: DataTypes.INTEGER,
|
|
328
299
|
allowNull: false, // Allow null feilds
|
|
329
300
|
},
|
|
301
|
+
sort: DataTypes.STRING,
|
|
330
302
|
createdAt: {
|
|
331
303
|
type: DataTypes.DATE,
|
|
332
304
|
allowNull: false,
|
|
@@ -350,7 +322,7 @@ Fruit.options = {
|
|
|
350
322
|
// Option 1: Allow all methods
|
|
351
323
|
defaultEndpoint: true,
|
|
352
324
|
|
|
353
|
-
|
|
325
|
+
// Option 2: Allow with specific per methods
|
|
354
326
|
defaultEndpoint: {
|
|
355
327
|
GET: true,
|
|
356
328
|
POST: false,
|
|
@@ -367,7 +339,6 @@ Fruit.options = {
|
|
|
367
339
|
},
|
|
368
340
|
|
|
369
341
|
limitRows: 100, // Limit rows default 100
|
|
370
|
-
|
|
371
342
|
};
|
|
372
343
|
|
|
373
344
|
// Example Finder by id (ORM), Learn more: https://sequelize.org/docs/v6/core-concepts/model-querying-finders/
|
|
@@ -375,7 +346,6 @@ function exampleFindOneFruitById(id) {
|
|
|
375
346
|
return Fruit.findOne({ where: { id: id } });
|
|
376
347
|
}
|
|
377
348
|
|
|
378
|
-
|
|
379
349
|
// Example Raw Query with Model Instances. This allows you to easily map a query to a predefined model
|
|
380
350
|
function exampleGetAllFruitWithModelInstance(id) {
|
|
381
351
|
return Fruit.query("SELECT * FROM fruit", {
|
|
@@ -400,19 +370,98 @@ module.exports = {
|
|
|
400
370
|
...
|
|
401
371
|
};
|
|
402
372
|
```
|
|
403
|
-
#### That's cool! It's like magic creating The endpoints for you (CRUD) ✨
|
|
404
373
|
|
|
405
|
-
|
|
374
|
+
## Retrieving data with Query String
|
|
375
|
+
|
|
376
|
+
Now you can add Query String with Conditional, Grouping and Ordering (Now Support Readonly for GET method)
|
|
377
|
+
|
|
378
|
+
### ✨ That's cool! It's like magic Creating The Endpoints for you (CRUD) ✨
|
|
379
|
+
|
|
380
|
+
<b style="font-size:12pt">For Example, Now!</b>, You can request to `/fruit` with methods GET, POST, PATCH and DELETE like this.
|
|
406
381
|
|
|
407
382
|
| Efficacy | Method | Endpoint | Body |
|
|
408
383
|
|:---------|:---------|:-----------------------|:-----------|
|
|
409
384
|
| Create | POST | /fruit | { } |
|
|
410
385
|
| Read | GET | /fruit | No |
|
|
411
|
-
| Read | GET | /fruit/:id | No |
|
|
412
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 |
|
|
413
390
|
| Update | PATCH | /fruit/:id | { } |
|
|
414
391
|
| Delete | DELETE | /fruit/:id | No |
|
|
415
392
|
|
|
393
|
+
Add some Basic Conditions, Grouping and Ordering with `QUERY STRING` under GET methods<br/>
|
|
394
|
+
|
|
395
|
+
Retrieving `fruit` data with GET : `/fruit?someField=[eq,1]&groupby=[id]&orderby=[id,desc]`
|
|
396
|
+
|
|
397
|
+
***For Example :***
|
|
398
|
+
|
|
399
|
+
```java
|
|
400
|
+
// WHERE Conditions
|
|
401
|
+
GET: /fruit?id=1 // id = 1
|
|
402
|
+
GET: /fruit?isActived=[eq,1] // isActived = 1
|
|
403
|
+
GET: /fruit?fruitName=[like,Banana%] // fruitName LIKE 'Banana%' (Not allow with date, time)
|
|
404
|
+
GET: /fruit?cost=[gt,50]&qty=[lt,10] // cost > 50 AND qty < 10
|
|
405
|
+
GET: /fruit/10/0?qty=[lt,10] // qty < 10 LIMIT 0,10
|
|
406
|
+
|
|
407
|
+
// Grouping
|
|
408
|
+
GET: /fruit?groupby=id // GROUP BY id
|
|
409
|
+
GET: /fruit?groupby=[id,fruitName] // GROUP BY id, fruitName
|
|
410
|
+
|
|
411
|
+
// Ordering
|
|
412
|
+
GET: /fruit?oderby=id // ORDER BY id ASC
|
|
413
|
+
GET: /fruit?oderby=[sort,desc] // ORDER BY sort DESC
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
For usage avariable:
|
|
417
|
+
|
|
418
|
+
```java
|
|
419
|
+
// Basics conditions
|
|
420
|
+
3 // = 3
|
|
421
|
+
[eq, 3] // = 3
|
|
422
|
+
[ne, 20] // != 20
|
|
423
|
+
[is, null] // IS NULL
|
|
424
|
+
[not, null] // IS NOT NULL
|
|
425
|
+
[or, [5, 6]] // (someField = 5) OR (someField = 6) // Not support NULL value
|
|
426
|
+
|
|
427
|
+
// Number comparisons conditions
|
|
428
|
+
[gt, 6] // > 6
|
|
429
|
+
[gte, 6] // >= 6
|
|
430
|
+
[lt, 10] // < 10
|
|
431
|
+
[lte, 10] // <= 10
|
|
432
|
+
[between, [6, 10]] // BETWEEN 6 AND 10
|
|
433
|
+
[between, [2025-01-01, 2025-04-30]] // BETWEEN '2025-01-01 00:00:00' AND '2025-04-30 23:59:59'
|
|
434
|
+
// When assign Datetime format will only support field datatype is `Date`, `Datetime`, `Time`
|
|
435
|
+
|
|
436
|
+
// OR You can assign Datetime like this.
|
|
437
|
+
[between, [2025-01-01 12:00:00, 2025-04-30 15:00:00]] // BETWEEN '2025-01-01 12:00:00' AND '2025-04-30 15:00:00'
|
|
438
|
+
|
|
439
|
+
[notBetween, [11, 15]] // NOT BETWEEN 11 AND 15
|
|
440
|
+
|
|
441
|
+
// Other operators conditions
|
|
442
|
+
[in, [1, 2, 3]], // IN [1, 2, 3]
|
|
443
|
+
[notIn, [1, 2, 3]], // NOT IN [1, 2, 3]
|
|
444
|
+
[like, %hat] // LIKE '%hat' (Avoid use #, % and %<Number> between wording)
|
|
445
|
+
// Becuase URL will be decoded it, Reccommand use startsWith, endsWith and substring when you need assing Number value)
|
|
446
|
+
// And NOT SUPPORT field datatype is `date`, `datetime`, `time`. Should be use with datatype is `String` or `Number` it work!.
|
|
447
|
+
|
|
448
|
+
[notLike, %hat] // NOT LIKE '%hat'
|
|
449
|
+
[startsWith, hat] // LIKE 'hat%'
|
|
450
|
+
[endsWith, hat] // LIKE '%hat'
|
|
451
|
+
[substring, hat] // LIKE '%hat%'
|
|
452
|
+
|
|
453
|
+
// Grouping
|
|
454
|
+
groupby=id // GROUP BY id
|
|
455
|
+
groupby=[id] // ORDER BY id
|
|
456
|
+
groupby=[id, fruitName] // ORDER BY id, fruitName
|
|
457
|
+
|
|
458
|
+
// Ordering
|
|
459
|
+
oderby=id // ORDER BY id ASC (Basic usage default Ascending)
|
|
460
|
+
oderby=[id, asc] // ORDER BY id ASC
|
|
461
|
+
oderby=[id, desc] // ORDER BY id ASC
|
|
462
|
+
oderby=[[id, desc], [sort, asc]] // ORDER BY id DESC, sort ASC
|
|
463
|
+
```
|
|
464
|
+
|
|
416
465
|
## # Transactions
|
|
417
466
|
|
|
418
467
|
Sequelize does not use transactions by default. However, for production-ready usage of Sequelize, you should definitely configure Sequelize to use transactions.
|
|
@@ -500,7 +549,306 @@ Fruit.transaction(
|
|
|
500
549
|
});
|
|
501
550
|
```
|
|
502
551
|
|
|
503
|
-
|
|
552
|
+
# Endpoints
|
|
553
|
+
|
|
554
|
+
The `endpoints` keep the endpoints basic request files currently support `GET`, `POST`, `PUT`, `PATCH` and `DELETE`.
|
|
555
|
+
|
|
556
|
+
So, you might create new endpoints with constant `endpoint` object variable in `src/endpoints/` folder and file neme must be end with `-endpoints.js`
|
|
557
|
+
|
|
558
|
+
```sh
|
|
559
|
+
$ beech make endpointName
|
|
560
|
+
```
|
|
561
|
+
You might using [special] `-R, --require` for choose Model(s) used for that endpoint.
|
|
562
|
+
|
|
563
|
+
***For Example :***
|
|
564
|
+
|
|
565
|
+
📂 endpoints/fruit-endpoints.js
|
|
566
|
+
```js
|
|
567
|
+
// Require Model schema, Function & Others
|
|
568
|
+
const { Fruit } = require("@/models/Fruit");
|
|
569
|
+
|
|
570
|
+
exports.init = () => {
|
|
571
|
+
|
|
572
|
+
// GET method
|
|
573
|
+
endpoint.get("/fruit", Credentials, async (req, res) => {
|
|
574
|
+
// example call Fruit model for get data
|
|
575
|
+
res.json({
|
|
576
|
+
code: 200,
|
|
577
|
+
status: "SUCCESS",
|
|
578
|
+
results: await Fruit.findAll();
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
// POST method
|
|
584
|
+
endpoint.post("/fruit", Credentials, (req, res) => {
|
|
585
|
+
// @response
|
|
586
|
+
res.json({
|
|
587
|
+
code: 200,
|
|
588
|
+
status: "SUCCESS",
|
|
589
|
+
message: "POST request at /fruit",
|
|
590
|
+
result: {
|
|
591
|
+
id: req.body.id,
|
|
592
|
+
name: req.body.name,
|
|
593
|
+
},
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
// PUT method
|
|
599
|
+
endpoint.put("/fruit/:id", Credentials, (req, res) => {
|
|
600
|
+
// @response
|
|
601
|
+
res.json({
|
|
602
|
+
code: 200,
|
|
603
|
+
status: "SUCCESS",
|
|
604
|
+
message: "PUT request at /fruit/" + req.params.id,
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
// DELETE method
|
|
610
|
+
endpoint.delete("/fruit/:id", Credentials, (req, res) => {
|
|
611
|
+
// @response
|
|
612
|
+
res.json({
|
|
613
|
+
code: 200,
|
|
614
|
+
status: "SUCCESS",
|
|
615
|
+
message: "DELETE request at /fruit/" + req.params.id,
|
|
616
|
+
});
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
...
|
|
620
|
+
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
# JWT Broken Role
|
|
625
|
+
|
|
626
|
+
The **JWT Broken Role** mechanism provides a flexible way to bypass or override role-based authorization rules when using JWT.
|
|
627
|
+
|
|
628
|
+
#### It can be applied in three different levels, depending on your use case.
|
|
629
|
+
|
|
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 |
|
|
635
|
+
|
|
636
|
+
This multi-layer approach allows you to design **secure, flexible, and maintainable JWT authorization flows**.
|
|
637
|
+
|
|
638
|
+
### 1. Global Configuration (passport.config.js)
|
|
639
|
+
|
|
640
|
+
You can define global JWT Broken Role rules that apply to all endpoints by configuring them in ``passport.config.js``.
|
|
641
|
+
```js
|
|
642
|
+
module.exports = {
|
|
643
|
+
// Enable JWT authentication
|
|
644
|
+
jwt_allow: true,
|
|
645
|
+
|
|
646
|
+
...
|
|
647
|
+
|
|
648
|
+
// Global JWT broken role configuration
|
|
649
|
+
jwt_broken_role: [
|
|
650
|
+
{
|
|
651
|
+
role: [0, 2, 4],
|
|
652
|
+
email: "john.doe@company.com",
|
|
653
|
+
}, // Basic role matching
|
|
654
|
+
|
|
655
|
+
{
|
|
656
|
+
role: { $in: [0, 2, 4] },
|
|
657
|
+
email: { $regex: /@company\.com$/ },
|
|
658
|
+
}, // Advanced matching using operators
|
|
659
|
+
],
|
|
660
|
+
|
|
661
|
+
...
|
|
662
|
+
};
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
**Use cases**
|
|
666
|
+
|
|
667
|
+
- Apply common authorization exceptions across the entire application
|
|
668
|
+
- Support both simple role arrays and advanced operators (``$in``, ``$regex``, etc.)
|
|
669
|
+
|
|
670
|
+
### 2. Model-Level Configuration (Per HTTP Method)
|
|
671
|
+
|
|
672
|
+
You can configure JWT and Broken Role rules per model and per HTTP method using model options.
|
|
673
|
+
|
|
674
|
+
📂 src/models/Fruit.js
|
|
675
|
+
```js
|
|
676
|
+
Fruit.options = {
|
|
677
|
+
...
|
|
678
|
+
|
|
679
|
+
// Auto-endpoint configuration by HTTP method
|
|
680
|
+
defaultEndpoint: {
|
|
681
|
+
GET: true, // Enable auto-generated GET endpoint
|
|
682
|
+
|
|
683
|
+
POST: {
|
|
684
|
+
allow: true,
|
|
685
|
+
jwt: {
|
|
686
|
+
allow: true,
|
|
687
|
+
broken_role: [
|
|
688
|
+
{ role: [5, 6] },
|
|
689
|
+
],
|
|
690
|
+
},
|
|
691
|
+
},
|
|
692
|
+
|
|
693
|
+
PATCH: {
|
|
694
|
+
allow: true,
|
|
695
|
+
jwt: {
|
|
696
|
+
allow: true,
|
|
697
|
+
broken_role: [
|
|
698
|
+
{ role: [5, 6, 9] },
|
|
699
|
+
{ department: "IT" },
|
|
700
|
+
],
|
|
701
|
+
},
|
|
702
|
+
},
|
|
703
|
+
|
|
704
|
+
DELETE: false, // Disable auto-generated DELETE endpoint
|
|
705
|
+
},
|
|
706
|
+
|
|
707
|
+
...
|
|
708
|
+
};
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
**Use cases**
|
|
712
|
+
|
|
713
|
+
- Fine-grained access control per HTTP method
|
|
714
|
+
- Different role requirements for POST, PATCH, etc.
|
|
715
|
+
- Combine role-based and attribute-based conditions
|
|
716
|
+
|
|
717
|
+
### 3. Endpoint-Level Configuration (Credentials Middleware)
|
|
718
|
+
|
|
719
|
+
For maximum flexibility, you can define Broken Role rules directly on a specific endpoint using the Credentials middleware.
|
|
720
|
+
|
|
721
|
+
📂 endpoints/custom-fruit-endpoints.js
|
|
722
|
+
```js
|
|
723
|
+
const { Fruit } = require("@/models/Fruit");
|
|
724
|
+
|
|
725
|
+
endpoint.delete(
|
|
726
|
+
"/destroy-fruit-by-id/:id",
|
|
727
|
+
Credentials([{ role: [5, 9] }]),
|
|
728
|
+
async (req, res) => {
|
|
729
|
+
|
|
730
|
+
// Only JWT tokens with role level 5 or 9 are allowed
|
|
731
|
+
|
|
732
|
+
const deleted = await Fruit.destroy({
|
|
733
|
+
where: {
|
|
734
|
+
id: req.params.id,
|
|
735
|
+
},
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
console.log("result:", deleted);
|
|
739
|
+
|
|
740
|
+
// @response
|
|
741
|
+
}
|
|
742
|
+
);
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
**Use cases**
|
|
746
|
+
|
|
747
|
+
- One-off or custom endpoints
|
|
748
|
+
- Highly specific authorization rules
|
|
749
|
+
- Override global or model-level behavior when necessary
|
|
750
|
+
|
|
751
|
+
## Supported Basic & Operators Usage
|
|
752
|
+
|
|
753
|
+
The JWT Broken Role system supports both basic value matching and advanced operators for flexible authorization rules.
|
|
754
|
+
|
|
755
|
+
| Operator | Meaning | Description |
|
|
756
|
+
|:---------|:-------------------|:--------------------------------------------------------------|
|
|
757
|
+
| $eq | equal | Matches when the value is equal |
|
|
758
|
+
| $ne | not equal | Matches when the value is not equal |
|
|
759
|
+
| $in | value IN array | Matches when the value exists in the specified array |
|
|
760
|
+
| $not | value NOT IN array | Matches when the value does not exist in the specified array |
|
|
761
|
+
| $regex | regex match | Matches using a regular expression |
|
|
762
|
+
| $fn | custom function | Matches using a custom evaluation function |
|
|
763
|
+
|
|
764
|
+
## Evaluation Logic (Very Important !)
|
|
765
|
+
```js
|
|
766
|
+
[
|
|
767
|
+
{ rule1 AND rule1 },
|
|
768
|
+
{ rule2 AND rule2 }
|
|
769
|
+
]
|
|
770
|
+
⬇
|
|
771
|
+
OR
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
## Basic Usage for Examples :
|
|
775
|
+
|
|
776
|
+
```js
|
|
777
|
+
Credentials([
|
|
778
|
+
{ role: [9] }, // Only role check
|
|
779
|
+
{ email: 'a@b.com' }, // OR email
|
|
780
|
+
])
|
|
781
|
+
|
|
782
|
+
// Common Matching Patterns
|
|
783
|
+
{ role: [1, 2, 3] } // IN
|
|
784
|
+
{ role: 1 } // Equal
|
|
785
|
+
{ email: 'a@b.com' }
|
|
786
|
+
{ department: 'IT' }
|
|
787
|
+
{ status: ['active'] } // Dynamic key
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
## Operators Usage for Examples :
|
|
791
|
+
|
|
792
|
+
```js
|
|
793
|
+
// Equal with Role
|
|
794
|
+
Credentials([
|
|
795
|
+
{
|
|
796
|
+
role: { $eq: [1, 2, 5] }
|
|
797
|
+
}
|
|
798
|
+
])
|
|
799
|
+
|
|
800
|
+
// Not Equal with Role
|
|
801
|
+
Credentials([
|
|
802
|
+
{
|
|
803
|
+
role: { $ne: [0, 3] }
|
|
804
|
+
}
|
|
805
|
+
])
|
|
806
|
+
|
|
807
|
+
// Regex with Email domain
|
|
808
|
+
Credentials([
|
|
809
|
+
{
|
|
810
|
+
email: { $regex: /@company\.com$/ }
|
|
811
|
+
}
|
|
812
|
+
])
|
|
813
|
+
|
|
814
|
+
// IN with Role
|
|
815
|
+
Credentials([
|
|
816
|
+
{
|
|
817
|
+
role: { $in: [1, 2, 4] }
|
|
818
|
+
}
|
|
819
|
+
])
|
|
820
|
+
|
|
821
|
+
// NOT IN with Role
|
|
822
|
+
Credentials([
|
|
823
|
+
{
|
|
824
|
+
role: { $not: [0, 3] }
|
|
825
|
+
}
|
|
826
|
+
])
|
|
827
|
+
|
|
828
|
+
// Multiple OR rules
|
|
829
|
+
Credentials([
|
|
830
|
+
{
|
|
831
|
+
role: { $in: [1] },
|
|
832
|
+
email: 'john.doe@company.com'
|
|
833
|
+
},
|
|
834
|
+
// OR
|
|
835
|
+
{
|
|
836
|
+
role: { $in: [4] },
|
|
837
|
+
email: { $regex: /@company\.com$/ }
|
|
838
|
+
},
|
|
839
|
+
])
|
|
840
|
+
|
|
841
|
+
// Custom Function
|
|
842
|
+
Credentials([
|
|
843
|
+
{
|
|
844
|
+
role: {
|
|
845
|
+
$fn: (value, user) => value >= 4 && user.department === 'IT'
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
])
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
# Helpers
|
|
504
852
|
|
|
505
853
|
The `helpers` keep the files of functions for process specific something in the project. So, you might create the `helpers` in path `src/helpers` folder.
|
|
506
854
|
|
|
@@ -508,9 +856,9 @@ The `helpers` keep the files of functions for process specific something in the
|
|
|
508
856
|
$ beech make helperName --helper
|
|
509
857
|
```
|
|
510
858
|
|
|
511
|
-
***Example:***
|
|
859
|
+
***For Example :***
|
|
512
860
|
|
|
513
|
-
📂 TextEditor.js
|
|
861
|
+
📂 helpers/TextEditor.js
|
|
514
862
|
```js
|
|
515
863
|
module.exports = {
|
|
516
864
|
|
|
@@ -598,7 +946,7 @@ module.exports = {
|
|
|
598
946
|
|
|
599
947
|
};
|
|
600
948
|
```
|
|
601
|
-
|
|
949
|
+
### Request Token
|
|
602
950
|
**Authentication structure :** Simple ``users`` table:
|
|
603
951
|
```
|
|
604
952
|
==============================================================
|
|
@@ -647,10 +995,10 @@ headers: Authorization: Bearer <your_token>
|
|
|
647
995
|
}
|
|
648
996
|
```
|
|
649
997
|
|
|
650
|
-
# Beech
|
|
998
|
+
# Beech Guard
|
|
651
999
|
You can easy using 2 Factor authenticate with ```guard_field``` inside ```passport.config.js``` file and add your Guard field ex: ```2fa``` field for Authenticate Conditions.
|
|
652
1000
|
|
|
653
|
-
## #
|
|
1001
|
+
## # Guard (2FA, Other)
|
|
654
1002
|
|
|
655
1003
|
📂 passport.config.js
|
|
656
1004
|
```js
|
|
@@ -659,7 +1007,7 @@ module.exports = {
|
|
|
659
1007
|
|
|
660
1008
|
guard: {
|
|
661
1009
|
// Other fields add for authenticate, exmaple ["pin", "hint", "2fa"]
|
|
662
|
-
guard_field: ["2fa"], 👈 // your feild guard.
|
|
1010
|
+
guard_field: ["2fa"], 👈 // your feild guard. (Disabled to remove it.)
|
|
663
1011
|
|
|
664
1012
|
...
|
|
665
1013
|
},
|
|
@@ -680,7 +1028,7 @@ module.exports = {
|
|
|
680
1028
|
guard: {
|
|
681
1029
|
...
|
|
682
1030
|
|
|
683
|
-
// Advanced guard
|
|
1031
|
+
// Advanced guard to Request (Needed some logical from front-end)
|
|
684
1032
|
advanced_guard: {
|
|
685
1033
|
allow: false, 👈 // advanced guard allow for All Endpoint.
|
|
686
1034
|
entity: "", // default entity `timing`
|
|
@@ -709,26 +1057,42 @@ $ npm install --save beech-auth0 moment
|
|
|
709
1057
|
# Yarn
|
|
710
1058
|
$ yarn add beech-auth0 moment
|
|
711
1059
|
```
|
|
712
|
-
Now! you can add some logic.
|
|
1060
|
+
Now! you can add some logic like this.
|
|
1061
|
+
|
|
1062
|
+
- Import packages
|
|
1063
|
+
|
|
713
1064
|
```js
|
|
1065
|
+
// CommonJS
|
|
714
1066
|
const { Auth0 } = require("beech-auth0");
|
|
715
1067
|
const moment = require("moment");
|
|
716
1068
|
|
|
1069
|
+
// ES6
|
|
1070
|
+
import { Auth0 } from "beech-auth0";
|
|
1071
|
+
import moment from "moment";
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
- Get unix time with momentJS
|
|
1075
|
+
|
|
1076
|
+
```js
|
|
717
1077
|
// Get UNIX TIME with moment
|
|
718
1078
|
let unix_time = moment().unix();
|
|
1079
|
+
```
|
|
719
1080
|
|
|
1081
|
+
- Get hashing with Beech Auth0
|
|
1082
|
+
|
|
1083
|
+
```js
|
|
720
1084
|
// Auth0 Policy.
|
|
721
1085
|
Auth0(unix_time, 'your_advance_guard_secret', (error, hashing) => {
|
|
722
|
-
|
|
1086
|
+
|
|
723
1087
|
// Your XHR request for All Endpoint.
|
|
724
1088
|
POST: "/authentication"
|
|
725
|
-
headers: timing: hashing
|
|
1089
|
+
headers: { timing: hashing } 👈 // Assign advance guard entity to headers with callback hashing.
|
|
726
1090
|
|
|
727
1091
|
});
|
|
728
1092
|
|
|
729
1093
|
```
|
|
730
1094
|
|
|
731
|
-
## # Beech User
|
|
1095
|
+
## # Beech User Authentication Managements ###
|
|
732
1096
|
You can easy management `users` data with Beech, Only ```Store, Update``` NO ```Delete```, Anything you can make DELETE endpoint by yourself
|
|
733
1097
|
|
|
734
1098
|
```js
|
|
@@ -1014,8 +1378,6 @@ endpoint.get("/banana", specificDup1, (req, res) => {
|
|
|
1014
1378
|
...
|
|
1015
1379
|
```
|
|
1016
1380
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
1381
|
# Databases managements
|
|
1020
1382
|
|
|
1021
1383
|
## # Migrations & Seeder
|
|
@@ -1050,6 +1412,7 @@ Before continuing further we will need to tell CLI how to connect to database. T
|
|
|
1050
1412
|
"password": null,
|
|
1051
1413
|
"database": "database_development",
|
|
1052
1414
|
"host": "127.0.0.1",
|
|
1415
|
+
"port": 3306, // IF your another port
|
|
1053
1416
|
"dialect": "mysql"
|
|
1054
1417
|
},
|
|
1055
1418
|
"test": {
|
|
@@ -1098,7 +1461,12 @@ Until this step, we haven't inserted anything into the database. We have just cr
|
|
|
1098
1461
|
|
|
1099
1462
|
- **Migrate Down** : you can use `db:migrate:undo`, this command will revert most recent migration.
|
|
1100
1463
|
```sh
|
|
1464
|
+
// Step to undo
|
|
1101
1465
|
$ npx sequelize-cli db:migrate:undo
|
|
1466
|
+
|
|
1467
|
+
// All to undo
|
|
1468
|
+
$ npx sequelize-cli db:migrate:undo:all
|
|
1469
|
+
|
|
1102
1470
|
```
|
|
1103
1471
|
|
|
1104
1472
|
## # Creating First Seeder
|
|
@@ -1147,9 +1515,9 @@ Test using [Jest](https://jestjs.io/en/) for testing the project. Jest is a deli
|
|
|
1147
1515
|
|
|
1148
1516
|
So, When you make the new endpoints it's automatic create test file end with `.spec.js` in `__test__` folder with constant `baseUrl` variable and `axios` package.
|
|
1149
1517
|
|
|
1150
|
-
Example
|
|
1518
|
+
***For Example :***
|
|
1151
1519
|
|
|
1152
|
-
📂 fruit-endpoints.spec.js
|
|
1520
|
+
📂 \_\_test\_\_/unit/endpoints/fruit-endpoints.spec.js
|
|
1153
1521
|
```js
|
|
1154
1522
|
const endpoint = baseUrl.concat("/fruit");
|
|
1155
1523
|
|
|
@@ -1180,7 +1548,7 @@ Docker builds images automatically by reading the instructions from a Dockerfile
|
|
|
1180
1548
|
|
|
1181
1549
|
📂 Dockerfile
|
|
1182
1550
|
```js
|
|
1183
|
-
FROM node:
|
|
1551
|
+
FROM node:18-alpine
|
|
1184
1552
|
ENV NODE_ENV=production
|
|
1185
1553
|
WORKDIR /usr/src/api
|
|
1186
1554
|
COPY ["package.json", "package-lock.json*", "./"]
|