beech-api 3.8.0 → 3.9.75

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.
Files changed (59) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1715 -1244
  3. package/index.js +2 -2
  4. package/package.json +92 -83
  5. package/packages/cli/beech +9 -10
  6. package/packages/cli/bin/beech-app.js +389 -389
  7. package/packages/cli/bin/beech-service.js +262 -132
  8. package/packages/cli/core/auth/Credentials.js +174 -147
  9. package/packages/cli/core/auth/Passport.js +664 -592
  10. package/packages/cli/core/auth/_Request.js +12 -12
  11. package/packages/cli/core/configure/_gitignore +16 -15
  12. package/packages/cli/core/configure/_sequelizerc +9 -9
  13. package/packages/cli/core/configure/app.config-basic.js +55 -55
  14. package/packages/cli/core/configure/app.config-sequelize.js +88 -88
  15. package/packages/cli/core/configure/beech.config.js +9 -9
  16. package/packages/cli/core/configure/global.config-basic.js +8 -8
  17. package/packages/cli/core/configure/global.config-sequelize.js +8 -8
  18. package/packages/cli/core/configure/jest.config.js +6 -6
  19. package/packages/cli/core/configure/jsconfig.json +8 -7
  20. package/packages/cli/core/configure/passport.config.js +97 -97
  21. package/packages/cli/core/databases/mysql.js +94 -95
  22. package/packages/cli/core/databases/sequelize.js +187 -179
  23. package/packages/cli/core/databases/test.js +251 -170
  24. package/packages/cli/core/file-walk/file-walk.js +35 -35
  25. package/packages/cli/core/generator/_endpoints +15 -15
  26. package/packages/cli/core/generator/_endpoints_basic +42 -42
  27. package/packages/cli/core/generator/_help +26 -18
  28. package/packages/cli/core/generator/_help_create +10 -10
  29. package/packages/cli/core/generator/_help_service +10 -10
  30. package/packages/cli/core/generator/_helpers +9 -9
  31. package/packages/cli/core/generator/_helpers_basic +22 -22
  32. package/packages/cli/core/generator/_models +6 -29
  33. package/packages/cli/core/generator/_models_basic +13 -22
  34. package/packages/cli/core/generator/_package +23 -20
  35. package/packages/cli/core/generator/_scheduler +32 -22
  36. package/packages/cli/core/generator/_spec +29 -29
  37. package/packages/cli/core/generator/index.js +1081 -738
  38. package/packages/cli/core/helpers/2fa.js +106 -85
  39. package/packages/cli/core/helpers/math.js +115 -103
  40. package/packages/cli/core/helpers/poolEntity.js +103 -59
  41. package/packages/cli/core/index.js +264 -188
  42. package/packages/cli/core/middleware/express/duplicateRequest.js +16 -12
  43. package/packages/cli/core/middleware/express/jwtCheckAllow.js +86 -68
  44. package/packages/cli/core/middleware/express/rateLimit.js +29 -17
  45. package/packages/cli/core/middleware/express/slowDown.js +2 -2
  46. package/packages/cli/core/middleware/index.js +6 -6
  47. package/packages/cli/core/middleware/origin/guard/advance.js +75 -74
  48. package/packages/cli/core/middleware/origin/whitelist/cors.js +94 -94
  49. package/packages/cli/core/services/http.express.js +481 -441
  50. package/packages/cli/core/test/check-node.js +21 -0
  51. package/packages/cli/core/test/utils.js +7 -7
  52. package/packages/cli/entry +10 -0
  53. package/packages/lib/index.js +6 -6
  54. package/packages/lib/src/endpoint.js +947 -532
  55. package/packages/lib/src/guard.js +60 -60
  56. package/packages/lib/src/salt.js +3 -3
  57. package/packages/lib/src/schema.js +96 -93
  58. package/packages/lib/src/specificExpress.js +7 -7
  59. package/packages/lib/src/user.js +271 -271
@@ -1,188 +1,264 @@
1
- global.appRoot = require("app-root-path");
2
- const moment = require("moment");
3
- const { performance } = require("perf_hooks");
4
- const moduleAlias = require("module-alias");
5
- moduleAlias.addAlias("@", appRoot + "/src");
6
- const _express_ = require("express");
7
- global._app_ = _express_();
8
- // Compression
9
- const compression = require("compression");
10
- _app_.use(compression());
11
- // Helmet
12
- const helmet = require("helmet");
13
- _app_.use(helmet());
14
- // CORS
15
- const cors = require("cors");
16
- global.endpoint = _express_.Router();
17
- const cookieParser = require("cookie-parser");
18
- const bodyParser = require("body-parser");
19
- const methodOverride = require("method-override");
20
- const expressSession = require("express-session");
21
- const expressValidator = require("express-validator");
22
- const globalVariable = require(appRoot + "/global.config.js");
23
- globalVariable.init();
24
- // Local environments
25
- global._config_ = require(appRoot + "/app.config");
26
- const _beech_ = require(appRoot + "/beech.config.js").defineConfig;
27
- global._publicPath_ = _beech_.base;
28
- const mySqlDbConnect = require("./databases/mysql");
29
- const SequelizeDbConnect = require("./databases/sequelize");
30
- // Rate Request middleware
31
- const { Limiter, Duplicater } = require("./middleware/index");
32
- endpoint.use(Limiter);
33
- endpoint.use(Duplicater);
34
- // Database test
35
- const {
36
- testConnectInProcess,
37
- filterDbIsTrue,
38
- disConnectTestDB,
39
- } = require("./databases/test");
40
- // create global sequelize object
41
- const { QueryTypes, DataTypes, Op } = require("sequelize");
42
- global.QueryTypes = QueryTypes;
43
- global.DataTypes = DataTypes;
44
- global.Op = Op;
45
- // Allow whitelist cors
46
- const { whitelist, sign, avg } = require("./middleware/index");
47
- _app_.use(cors({ origin: true, credentials: true }));
48
- _app_.use((req, res, next) => {
49
- whitelist(async (lists, originSensitive) => {
50
- sign(req, res, lists, originSensitive, (err) => {
51
- if (!err) {
52
- next();
53
- } else {
54
- throw err;
55
- }
56
- });
57
- });
58
- });
59
- // View engine
60
- _app_.use(bodyParser.json());
61
- _app_.use(bodyParser.urlencoded({ extended: true }));
62
- _app_.use(methodOverride());
63
- _app_.use(cookieParser());
64
- _app_.use(expressSession({
65
- secret: "surprise you mother f*cker",
66
- resave: true,
67
- saveUninitialized: true,
68
- }));
69
- _app_.use(expressValidator());
70
- // Dev. activity
71
- global._requestTime_ = moment(new Date()).format("YYYY-MM-DD HH:mm:ss");
72
- _app_.use((req, res, next) => {
73
- console.log(`[${_requestTime_}] : Request ${req.method} ${req.originalUrl}`);
74
- const t0 = performance.now();
75
- res.on('finish', () => {
76
- const responseTime = moment(new Date()).format("YYYY-MM-DD HH:mm:ss");
77
- const t1 = performance.now();
78
- const duration = (t1 - t0).toFixed(0);
79
- console.log(`[${responseTime}] : Response ${res.statusCode} (${duration}ms)`);
80
- });
81
- next();
82
- });
83
- // Check Syntax error.
84
- _app_.use((error, req, res, next) => {
85
- if (error instanceof SyntaxError) {
86
- res.status(400).json({
87
- ...error,
88
- status: "BAD_REQUEST",
89
- message: "Bad Request.",
90
- body: error.body,
91
- });
92
- } else {
93
- next();
94
- }
95
- });
96
- // Advance Guard
97
- _app_.use(avg);
98
- // Engine import
99
- const httpExpress = require("./services/http.express");
100
- const fileWalk = require("./file-walk/file-walk");
101
- // Passport initialization
102
- const authPassport = require("./auth/Passport");
103
- const passport = require("passport");
104
- _app_.use(passport.initialize());
105
- _app_.use(passport.session());
106
- passport.serializeUser((user, done) => {
107
- done(null, user);
108
- });
109
- passport.deserializeUser((user, done) => {
110
- done(null, user);
111
- });
112
- // Endpoint magic
113
- const { Base } = require("../../lib/index");
114
- // Read folder in ./src/endpoints/*
115
- const walk = require("walk");
116
- let jsfiles = [];
117
- let walker = walk.walk(appRoot + "/src/endpoints", { followLinks: false });
118
- // Walk file on push
119
- walker.on("file", (root, stat, next) => {
120
- jsfiles.push(root + "/" + stat.name);
121
- next();
122
- });
123
- // Walking
124
- walker.on("end", () => {
125
- init(jsfiles);
126
- });
127
- // Initialize the application
128
- init = async (jsfiles) => {
129
- try {
130
- const testConnectToDB = new Promise((resolve) => {
131
- filterDbIsTrue(_config_.database_config, (err, dbTruthy) => {
132
- if (err) {
133
- throw ("Config file crash.", err);
134
- }
135
- // leave data to disconnect database
136
- let leaveDataForDisconnect = dbTruthy.slice(0);
137
- // check db connect truthy length ?
138
- if (dbTruthy.length > 0) {
139
- testConnectInProcess(dbTruthy, dbTruthy.length, (err, result, dbs) => {
140
- if (err) {
141
- throw (" Failed  Database connect failed.", err);
142
- }
143
- if (result) {
144
- // Disconnect database
145
- disConnectTestDB(leaveDataForDisconnect, dbs, (err, disResult) => {
146
- if (err) {
147
- throw (" Failed  Testing Database connect failed.", err);
148
- }
149
- if (disResult) {
150
- // Disconnect and Next to real
151
- resolve(true);
152
- } else {
153
- throw err;
154
- }
155
- });
156
- }
157
- });
158
- } else {
159
- // Not ON connect, Next to real
160
- resolve(true);
161
- }
162
- });
163
- });
164
- Promise.all([testConnectToDB]).then(async (x) => {
165
- if (x[0]) {
166
- await (pool_base == "basic" ? new Promise((resolve) => resolve(mySqlDbConnect.connect())) : new Promise((resolve) => resolve(SequelizeDbConnect.connect())));
167
- await authPassport.init().then(async (x) => {
168
- if (x[0]) {
169
- throw x[0];
170
- } else {
171
- await new Promise((resolve) => resolve(fileWalk.fileWalk(jsfiles)));
172
- await (pool_base == "basic" ? new Promise((resolve) => resolve()) : new Promise((resolve) => resolve(Base())));
173
- await new Promise((resolve) => {
174
- httpExpress.expressStart().then((expss) => {
175
- resolve(expss);
176
- });
177
- });
178
- }
179
- });
180
- }
181
- });
182
- } catch (error) {
183
- console.log(" Compile failed ", error);
184
- throw error;
185
- }
186
- };
187
- // Use router
188
- _app_.use(_publicPath_, endpoint);
1
+ global.appRoot = require("app-root-path");
2
+ const moment = require("moment");
3
+ const { performance } = require("perf_hooks");
4
+ const moduleAlias = require("module-alias");
5
+ moduleAlias.addAlias("@", appRoot + "/src");
6
+ const _express_ = require("express");
7
+ global._app_ = _express_();
8
+ // Compression
9
+ const compression = require("compression");
10
+ _app_.use(compression());
11
+ // Helmet
12
+ const helmet = require("helmet");
13
+ _app_.use(helmet());
14
+ // CORS
15
+ const cors = require("cors");
16
+ global.endpoint = _express_.Router();
17
+ const cookieParser = require("cookie-parser");
18
+ const methodOverride = require("method-override");
19
+ const expressSession = require("express-session");
20
+ const globalVariable = require(appRoot + "/global.config.js");
21
+ globalVariable.init();
22
+ // Local environments
23
+ global._config_ = require(appRoot + "/app.config");
24
+ const _beech_ = require(appRoot + "/beech.config.js").defineConfig;
25
+ global._publicPath_ = _beech_.base;
26
+ const mySqlDbConnect = require("./databases/mysql");
27
+ const SequelizeDbConnect = require("./databases/sequelize");
28
+ // Set limit payload for request body & multipart/form-data (multer)
29
+ const multer = require("multer");
30
+ const uploadAllowMethod = _beech_?.payload?.file?.uploadAllowMethod || ["POST", "PATCH", "PUT"];
31
+ const allowedTypes = _beech_?.payload?.file?.allowedTypes || []; // default: no allowed type
32
+ const fileLimitSize = _beech_?.payload?.file?.limit || 5 * 1024 * 1024; // 5MB
33
+ const uploadStrategy = multer({
34
+ limits: { fileSize: fileLimitSize },
35
+ fileFilter: (req, file, cb) => {
36
+ if (allowedTypes.length === 0) {
37
+ cb(new Error("INVALID_FILE_TYPE_ALLOW"), false); // Allow all file types if no allowed types specified
38
+ } else if (allowedTypes.includes(file.mimetype)) {
39
+ cb(null, true);
40
+ } else {
41
+ cb(new Error("INVALID_FILE_TYPE"), false);
42
+ }
43
+ }
44
+ }).any();
45
+ const jsonLimitSize = _beech_?.payload?.json?.limit || "100KB"; // json payload
46
+ const urlencodedLimitSize = _beech_?.payload?.urlencoded?.limit || "100KB"; // urlencoded payload (multipart/form-data)
47
+ const urlencodedExtended = !!(_beech_?.payload?.urlencoded?.extended ?? true);
48
+ _app_.use(_express_.urlencoded({ limit: urlencodedLimitSize, extended: urlencodedExtended })); // application/x-www-form-urlencoded payload
49
+ _app_.use(_express_.json({ limit: jsonLimitSize }));
50
+ _app_.use((req, res, next) => {
51
+ const isUploadMethod = uploadAllowMethod.includes(req.method);
52
+ const isMultipart = req.headers["content-type"]?.includes("multipart/form-data");
53
+ // Handle file upload for allowed methods
54
+ if (isUploadMethod) {
55
+ return uploadStrategy(req, res, (err) => {
56
+ if (err) return next(err);
57
+ next();
58
+ });
59
+ }
60
+ // Handle method not allowed for file upload
61
+ if (!isUploadMethod && isMultipart) {
62
+ return res.status(405).json({
63
+ code: 405,
64
+ status: "METHOD_NOT_ALLOWED_FOR_UPLOAD",
65
+ message: _config_.main_config?.dev ? `File upload is not allowed for ${req.method} method.` : "Method Not Allowed for file upload.",
66
+ });
67
+ }
68
+ // next to next middleware
69
+ next();
70
+ });
71
+ _app_.use((err, req, res, next) => {
72
+ // Handle payload too large error for JSON and URL-encoded
73
+ if (err.type === "entity.too.large") {
74
+ const isJson = req.headers["content-type"]?.includes("application/json");
75
+ const limitUsed = isJson ? jsonLimitSize : urlencodedLimitSize;
76
+ return res.status(413).json({
77
+ code: 413,
78
+ status: "PAYLOAD_TOO_LARGE",
79
+ message:_config_.main_config?.dev ? `${isJson ? 'JSON' : 'Form data'} too large, Max limit is ${limitUsed}` : "Payload Too Large.",
80
+ });
81
+ }
82
+ if (err.message === "INVALID_FILE_TYPE_ALLOW") {
83
+ return res.status(400).json({
84
+ code: 400,
85
+ status: "INVALID_FILE_TYPE_ALLOW",
86
+ message: "Invalid file type, No file types allowed.",
87
+ });
88
+ }
89
+ // Handle invalid file type error from multer
90
+ if (err.message === "INVALID_FILE_TYPE") {
91
+ return res.status(400).json({
92
+ code: 400,
93
+ status: "INVALID_FILE_TYPE",
94
+ message: _config_.main_config?.dev ? `Invalid file type, Allowed: ${allowedTypes.join(', ')}` : "Invalid file type.",
95
+ });
96
+ }
97
+ // Handle multer file size limit error
98
+ if (err.code === "LIMIT_FILE_SIZE") {
99
+ return res.status(413).json({
100
+ code: 413,
101
+ status: "PAYLOAD_TOO_LARGE",
102
+ message: _config_.main_config?.dev ? `File size too large, Max limit is ${fileLimitSize / 1024 / 1024}MB` : "Payload Too Large.",
103
+ });
104
+ }
105
+ // next to general error handler
106
+ next(err);
107
+ });
108
+ // Database test
109
+ const {
110
+ testConnectInProcess,
111
+ filterDbIsTrue,
112
+ disConnectTestDB,
113
+ } = require("./databases/test");
114
+ // create global sequelize object
115
+ const { QueryTypes, DataTypes, Op } = require("sequelize");
116
+ global.QueryTypes = QueryTypes;
117
+ global.DataTypes = DataTypes;
118
+ global.Op = Op;
119
+ // Allow whitelist cors
120
+ const { whitelist, sign, avg } = require("./middleware/index");
121
+ _app_.use(cors({ origin: true, credentials: true }));
122
+ _app_.use((req, res, next) => {
123
+ whitelist(async (lists, originSensitive) => {
124
+ sign(req, res, lists, originSensitive, (err) => {
125
+ if (!err) {
126
+ next();
127
+ } else {
128
+ throw err;
129
+ }
130
+ });
131
+ });
132
+ });
133
+ // View engine
134
+ _app_.use(methodOverride());
135
+ _app_.use(cookieParser());
136
+ _app_.use(expressSession({
137
+ secret: "surprise you mother f*cker",
138
+ resave: true,
139
+ saveUninitialized: true,
140
+ }));
141
+ // Dev. activity
142
+ global._requestTime_ = moment(new Date()).format("YYYY-MM-DD HH:mm:ss");
143
+ _app_.use((req, res, next) => {
144
+ console.log(`[${_requestTime_}] : Request ${req.method} ${req.originalUrl}`);
145
+ const t0 = performance.now();
146
+ res.on('finish', () => {
147
+ const responseTime = moment(new Date()).format("YYYY-MM-DD HH:mm:ss");
148
+ const t1 = performance.now();
149
+ const duration = (t1 - t0).toFixed(0);
150
+ console.log(`[${responseTime}] : Response ${res.statusCode} (${duration}ms)`);
151
+ });
152
+ next();
153
+ });
154
+ // Check Syntax error.
155
+ _app_.use((error, req, res, next) => {
156
+ if (error instanceof SyntaxError) {
157
+ res.status(400).json({
158
+ ...error,
159
+ status: "BAD_REQUEST",
160
+ message: "Bad Request.",
161
+ body: error.body,
162
+ });
163
+ } else {
164
+ next();
165
+ }
166
+ });
167
+ // Advance Guard
168
+ _app_.use(avg);
169
+ // Engine import
170
+ const httpExpress = require("./services/http.express");
171
+ const fileWalk = require("./file-walk/file-walk");
172
+ // Passport initialization
173
+ const authPassport = require("./auth/Passport");
174
+ const passport = require("passport");
175
+ _app_.use(passport.initialize());
176
+ _app_.use(passport.session());
177
+ passport.serializeUser((user, done) => {
178
+ done(null, user);
179
+ });
180
+ passport.deserializeUser((user, done) => {
181
+ done(null, user);
182
+ });
183
+ // Endpoint magic
184
+ const { Base } = require("../../lib/index");
185
+ // Read folder in ./src/endpoints/*
186
+ const walk = require("walk");
187
+ let jsfiles = [];
188
+ let walker = walk.walk(appRoot + "/src/endpoints", { followLinks: false });
189
+ // Walk file on push
190
+ walker.on("file", (root, stat, next) => {
191
+ jsfiles.push(root + "/" + stat.name);
192
+ next();
193
+ });
194
+ // Walking
195
+ walker.on("end", () => {
196
+ init(jsfiles);
197
+ });
198
+ // Initialize the application
199
+ init = async (jsfiles) => {
200
+ try {
201
+ const testConnectToDB = new Promise((resolve) => {
202
+ filterDbIsTrue(_config_.database_config, (err, dbTruthy) => {
203
+ if (err) {
204
+ throw ("Config file crash.", err);
205
+ }
206
+ // leave data to disconnect database
207
+ let leaveDataForDisconnect = dbTruthy.slice(0);
208
+ // check db connect truthy length ?
209
+ if (dbTruthy.length > 0) {
210
+ testConnectInProcess(dbTruthy, dbTruthy.length, (err, result, dbs) => {
211
+ if (err) {
212
+ throw (" Failed  Database connect failed.", err);
213
+ }
214
+ if (result) {
215
+ // Disconnect database
216
+ disConnectTestDB(leaveDataForDisconnect, dbs, (err, disResult) => {
217
+ if (err) {
218
+ throw (" Failed  Testing Database connect failed.", err);
219
+ }
220
+ if (disResult) {
221
+ // Disconnect and Next to real
222
+ resolve(true);
223
+ } else {
224
+ throw err;
225
+ }
226
+ });
227
+ }
228
+ });
229
+ } else {
230
+ // Not ON connect, Next to real
231
+ resolve(true);
232
+ }
233
+ });
234
+ });
235
+ Promise.all([testConnectToDB]).then(async (x) => {
236
+ if (x[0]) {
237
+ await (pool_base == "basic" ? new Promise((resolve) => resolve(mySqlDbConnect.connect())) : new Promise((resolve) => resolve(SequelizeDbConnect.connect())));
238
+ await authPassport.init().then(async (p) => {
239
+ if (p[0]) {
240
+ console.log("\n Init failed ", p[0]);
241
+ return;
242
+ //throw p[0];
243
+ } else {
244
+ await new Promise((resolve) => resolve(fileWalk.fileWalk(jsfiles)));
245
+ await (pool_base == "basic" ? new Promise((resolve) => resolve()) : new Promise((resolve) => resolve(Base())));
246
+ await new Promise((resolve) => {
247
+ httpExpress.expressStart().then((expss) => {
248
+ resolve(expss);
249
+ });
250
+ });
251
+ }
252
+ }).catch((err) => {
253
+ console.log(" Catch init failed ", err);
254
+ throw err;
255
+ });
256
+ }
257
+ });
258
+ } catch (error) {
259
+ console.log(" Compile failed ", error);
260
+ throw error;
261
+ }
262
+ };
263
+ // Use router
264
+ _app_.use(_publicPath_, endpoint);
@@ -1,12 +1,16 @@
1
- const _beech_ = require(appRoot + "/beech.config.js");
2
- const { duplicateRequest } = require("express-duplicate-request");
3
- const nextDuplicater = (req, res, next) => {
4
- next();
5
- };
6
- let configure = {
7
- expiration: _beech_.defineConfig.server.duplicateRequest ? _beech_.defineConfig.server.duplicateRequest.expiration : 0,
8
- };
9
- configure = { ...configure, ..._beech_.defineConfig.server.duplicateRequest };
10
- const Duplicater = configure.expiration ? duplicateRequest(configure) : nextDuplicater;
11
-
12
- module.exports = { Duplicater, duplicateRequest };
1
+ const _beech_ = require(appRoot + "/beech.config.js");
2
+ const { duplicateRequest } = require("express-duplicate-request");
3
+ const nextDuplicater = (req, res, next) => next();
4
+ const defaultConfigure = {
5
+ expiration: _beech_.defineConfig.server.duplicateRequest ? _beech_.defineConfig.server.duplicateRequest.expiration : 0,
6
+ };
7
+ const baseConfigure = {
8
+ ..._beech_.defineConfig.server.duplicateRequest, // Override default configure with user configure.
9
+ ...defaultConfigure,
10
+ };
11
+ const Duplicater = (more_configure = {}) => {
12
+ const config = { ...baseConfigure, ...more_configure };
13
+ return config.expiration ? duplicateRequest(config) : nextDuplicater;
14
+ };
15
+
16
+ module.exports = { Duplicater, duplicateRequest };
@@ -1,68 +1,86 @@
1
- const passport = require("passport");
2
-
3
- const checkRoleMiddleware = (options) => {
4
- return function (req, res, next) {
5
- if(!Array.isArray(options)) {
6
- return next();
7
- } else {
8
- passport.authenticate("jwt", {
9
- session: false,
10
- }, (err, user, info) => {
11
- // error check
12
- if (err) {
13
- console.log(err, info);
14
- return res.status(403).json({
15
- code: 403,
16
- status: "FORBIDDEN",
17
- message: "Forbidden: Insufficient role",
18
- info: {
19
- error: err,
20
- },
21
- });
22
- } else {
23
- let isPerfectly = true;
24
- options.forEach((element, key) => {
25
- let checkVal = Object.values(element).flat();
26
- let checkKey = Object.keys(element);
27
- if(user && user[checkKey]) {
28
- if(checkVal.includes(user[checkKey])) {
29
- if(options.length -1 === key) {
30
- if(isPerfectly) {
31
- // Perfectly
32
- return next();
33
- }
34
- }
35
- } else {
36
- if(isPerfectly) {
37
- res.status(403).json({
38
- code: 403,
39
- status: "FORBIDDEN",
40
- message: "Forbidden: Insufficient role",
41
- });
42
- }
43
- isPerfectly = false;
44
- }
45
- } else {
46
- if(isPerfectly) {
47
- res.status(403).json({
48
- code: 403,
49
- status: "FORBIDDEN",
50
- message: `No ${checkKey} assigned to token.`,
51
- });
52
- }
53
- isPerfectly = false;
54
- }
55
- });
56
- }
57
- })(req, res, next);
58
- }
59
- }
60
- }
61
-
62
- const checkRoleMiddlewareWithDefaultProject = (options) => {
63
- return function (req, res, next) {
64
- return checkRoleMiddleware(options)(req, res, next);
65
- }
66
- }
67
-
68
- module.exports = { checkRoleMiddleware, checkRoleMiddlewareWithDefaultProject }
1
+ const passport = require("passport");
2
+
3
+ const checkRoleMiddleware = (options) => {
4
+ return (req, res, next) => {
5
+ if(!Array.isArray(options)) {
6
+ // Perfectly with options is not type Array
7
+ return next();
8
+ } else {
9
+ passport.authenticate("jwt", {
10
+ session: false,
11
+ }, (err, user, info) => {
12
+ // error check
13
+ if (err) {
14
+ //console.log(err, info);
15
+ return res.status(403).json({
16
+ code: 403,
17
+ status: "FORBIDDEN",
18
+ message: "Forbidden: Insufficient role",
19
+ info: {
20
+ error: err,
21
+ },
22
+ });
23
+ } else {
24
+ if(!options.length) {
25
+ // Perfectly with no options
26
+ return next();
27
+ } else {
28
+ const allowed = options.some(rule => {
29
+ return Object.entries(rule).every(([key, condition]) => {
30
+ //console.log('----matchCondition(user?.[key], condition, user)-->>', matchCondition(user?.[key], condition, user));
31
+ return matchCondition(user?.[key], condition, user);
32
+ });
33
+ });
34
+ if (allowed) {
35
+ return next();
36
+ } else {
37
+ res.status(403).json({
38
+ code: 403,
39
+ status: "FORBIDDEN",
40
+ message: "Forbidden: Insufficient role",
41
+ info: {
42
+ status: "ROLE_NOT_ALLOWED",
43
+ error: "Insufficient role or User token does not have sufficient role.",
44
+ },
45
+ });
46
+ }
47
+ }
48
+ }
49
+ })(req, res, next);
50
+ }
51
+ }
52
+ }
53
+
54
+ const operators = {
55
+ $eq: (userValue, expected) => userValue === expected,
56
+ $ne: (userValue, expected) => userValue !== expected,
57
+ $in: (userValue, expected) => Array.isArray(expected) && expected.includes(userValue),
58
+ $not: (userValue, expected) => Array.isArray(expected) && !expected.includes(userValue),
59
+ $regex: (userValue, expected) =>
60
+ typeof userValue === 'string' && expected instanceof RegExp
61
+ ? expected.test(userValue)
62
+ : false,
63
+ $fn: (userValue, fn, user) => typeof fn === 'function' && fn(userValue, user),
64
+ };
65
+
66
+ const matchCondition = (userValue, condition, user) => {
67
+ if (typeof condition !== 'object' || condition instanceof RegExp || Array.isArray(condition)) {
68
+ return Array.isArray(condition)
69
+ ? operators.$in(userValue, condition)
70
+ : operators.$eq(userValue, condition);
71
+ }
72
+ // operator object
73
+ return Object.entries(condition).every(([op, expected]) => {
74
+ const handler = operators[op];
75
+ if (!handler) return false;
76
+ return handler(userValue, expected, user);
77
+ });
78
+ };
79
+
80
+ const checkRoleMiddlewareWithDefaultProject = (options) => {
81
+ return function (req, res, next) {
82
+ return checkRoleMiddleware(options)(req, res, next);
83
+ }
84
+ }
85
+
86
+ module.exports = { checkRoleMiddleware, checkRoleMiddlewareWithDefaultProject };