beech-api 3.7.23 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +490 -168
  2. package/index.js +2 -2
  3. package/package.json +8 -1
  4. package/packages/cli/beech +2 -2
  5. package/packages/cli/bin/beech-app.js +10 -8
  6. package/packages/cli/bin/beech-service.js +1 -1
  7. package/packages/cli/core/auth/Credentials.js +139 -89
  8. package/packages/cli/core/auth/Passport.js +264 -164
  9. package/packages/cli/core/auth/_Request.js +1 -1
  10. package/packages/cli/core/configure/app.config-basic.js +2 -2
  11. package/packages/cli/core/configure/app.config-sequelize.js +2 -2
  12. package/packages/cli/core/configure/beech.config.js +1 -0
  13. package/packages/cli/core/configure/passport.config.js +33 -13
  14. package/packages/cli/core/databases/sequelize.js +3 -0
  15. package/packages/cli/core/databases/test.js +5 -3
  16. package/packages/cli/core/generator/_endpoints +5 -9
  17. package/packages/cli/core/generator/_endpoints_basic +11 -8
  18. package/packages/cli/core/generator/_help +1 -1
  19. package/packages/cli/core/generator/_models +5 -4
  20. package/packages/cli/core/generator/_models_basic +2 -2
  21. package/packages/cli/core/generator/_package +5 -1
  22. package/packages/cli/core/generator/{_add-on → _scheduler} +1 -1
  23. package/packages/cli/core/generator/_spec +15 -10
  24. package/packages/cli/core/generator/index.js +19 -44
  25. package/packages/cli/core/helpers/2fa.js +85 -0
  26. package/packages/cli/core/helpers/math.js +55 -7
  27. package/packages/cli/core/helpers/poolEntity.js +29 -1
  28. package/packages/cli/core/index.js +65 -34
  29. package/packages/cli/core/middleware/express/duplicateRequest.js +12 -0
  30. package/packages/cli/core/middleware/express/jwtCheckAllow.js +68 -0
  31. package/packages/cli/core/middleware/express/rateLimit.js +17 -0
  32. package/packages/cli/core/middleware/express/slowDown.js +2 -0
  33. package/packages/cli/core/middleware/index.js +6 -0
  34. package/packages/cli/core/middleware/origin/guard/advance.js +74 -0
  35. package/packages/cli/core/{origin → middleware/origin}/whitelist/cors.js +15 -12
  36. package/packages/cli/core/services/http.express.js +116 -72
  37. package/packages/lib/index.js +3 -1
  38. package/packages/lib/src/endpoint.js +523 -89
  39. package/packages/lib/src/guard.js +61 -0
  40. package/packages/lib/src/schema.js +57 -26
  41. package/packages/lib/src/specificExpress.js +7 -0
  42. package/packages/lib/src/user.js +94 -18
  43. package/packages/cli/core/origin/index.js +0 -2
@@ -1,9 +1,17 @@
1
- const appRoot = require("app-root-path");
1
+ global.appRoot = require("app-root-path");
2
+ const moment = require("moment");
2
3
  const { performance } = require("perf_hooks");
3
4
  const moduleAlias = require("module-alias");
4
5
  moduleAlias.addAlias("@", appRoot + "/src");
5
6
  const _express_ = require("express");
6
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
7
15
  const cors = require("cors");
8
16
  global.endpoint = _express_.Router();
9
17
  const cookieParser = require("cookie-parser");
@@ -15,9 +23,15 @@ const globalVariable = require(appRoot + "/global.config.js");
15
23
  globalVariable.init();
16
24
  // Local environments
17
25
  global._config_ = require(appRoot + "/app.config");
26
+ const _beech_ = require(appRoot + "/beech.config.js").defineConfig;
27
+ global._publicPath_ = _beech_.base;
18
28
  const mySqlDbConnect = require("./databases/mysql");
19
29
  const SequelizeDbConnect = require("./databases/sequelize");
20
- // database test
30
+ // Rate Request middleware
31
+ const { Limiter, Duplicater } = require("./middleware/index");
32
+ endpoint.use(Limiter);
33
+ endpoint.use(Duplicater);
34
+ // Database test
21
35
  const {
22
36
  testConnectInProcess,
23
37
  filterDbIsTrue,
@@ -28,36 +42,63 @@ const { QueryTypes, DataTypes, Op } = require("sequelize");
28
42
  global.QueryTypes = QueryTypes;
29
43
  global.DataTypes = DataTypes;
30
44
  global.Op = Op;
31
- // allow whitelist cors
45
+ // Allow whitelist cors
46
+ const { whitelist, sign, avg } = require("./middleware/index");
32
47
  _app_.use(cors({ origin: true, credentials: true }));
33
- const { whitelist, sign } = require("./origin/index");
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
+ });
34
59
  // View engine
35
60
  _app_.use(bodyParser.json());
36
61
  _app_.use(bodyParser.urlencoded({ extended: true }));
37
62
  _app_.use(methodOverride());
38
63
  _app_.use(cookieParser());
39
- _app_.use(
40
- expressSession({
41
- secret: "surprise you mother f*cker",
42
- resave: true,
43
- saveUninitialized: true,
44
- })
45
- );
64
+ _app_.use(expressSession({
65
+ secret: "surprise you mother f*cker",
66
+ resave: true,
67
+ saveUninitialized: true,
68
+ }));
46
69
  _app_.use(expressValidator());
47
70
  // Dev. activity
71
+ global._requestTime_ = moment(new Date()).format("YYYY-MM-DD HH:mm:ss");
48
72
  _app_.use((req, res, next) => {
49
- console.log("Request URL:", req.method, req.originalUrl);
50
- var t0 = performance.now();
51
- res.on("finish", () => {
52
- var t1 = performance.now();
53
- console.log(`Responded with status : ${res.statusCode} (${(t1 - t0).toFixed(2)}ms)`);
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)`);
54
80
  });
55
81
  next();
56
82
  });
57
- // engine import
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
58
99
  const httpExpress = require("./services/http.express");
59
100
  const fileWalk = require("./file-walk/file-walk");
60
- // passport initialization
101
+ // Passport initialization
61
102
  const authPassport = require("./auth/Passport");
62
103
  const passport = require("passport");
63
104
  _app_.use(passport.initialize());
@@ -68,6 +109,8 @@ passport.serializeUser((user, done) => {
68
109
  passport.deserializeUser((user, done) => {
69
110
  done(null, user);
70
111
  });
112
+ // Endpoint magic
113
+ const { Base } = require("../../lib/index");
71
114
  // Read folder in ./src/endpoints/*
72
115
  const walk = require("walk");
73
116
  let jsfiles = [];
@@ -120,24 +163,13 @@ init = async (jsfiles) => {
120
163
  });
121
164
  Promise.all([testConnectToDB]).then(async (x) => {
122
165
  if (x[0]) {
123
- await (pool_base == "basic"
124
- ? new Promise((resolve) => resolve(mySqlDbConnect.connect()))
125
- : new Promise((resolve) => resolve(SequelizeDbConnect.connect())));
126
- await whitelist(async (lists, originSensitive) => {
127
- await _app_.use((req, res, next) => {
128
- sign(req, res, lists, originSensitive, (err) => {
129
- if (!err) {
130
- next();
131
- } else {
132
- throw err;
133
- }
134
- });
135
- });
166
+ await (pool_base == "basic" ? new Promise((resolve) => resolve(mySqlDbConnect.connect())) : new Promise((resolve) => resolve(SequelizeDbConnect.connect())));
136
167
  await authPassport.init().then(async (x) => {
137
168
  if (x[0]) {
138
169
  throw x[0];
139
170
  } else {
140
171
  await new Promise((resolve) => resolve(fileWalk.fileWalk(jsfiles)));
172
+ await (pool_base == "basic" ? new Promise((resolve) => resolve()) : new Promise((resolve) => resolve(Base())));
141
173
  await new Promise((resolve) => {
142
174
  httpExpress.expressStart().then((expss) => {
143
175
  resolve(expss);
@@ -145,7 +177,6 @@ init = async (jsfiles) => {
145
177
  });
146
178
  }
147
179
  });
148
- });
149
180
  }
150
181
  });
151
182
  } catch (error) {
@@ -153,5 +184,5 @@ init = async (jsfiles) => {
153
184
  throw error;
154
185
  }
155
186
  };
156
- // use router
157
- _app_.use(endpoint);
187
+ // Use router
188
+ _app_.use(_publicPath_, endpoint);
@@ -0,0 +1,12 @@
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 };
@@ -0,0 +1,68 @@
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 }
@@ -0,0 +1,17 @@
1
+ const _beech_ = require(appRoot + "/beech.config.js");
2
+ const rateLimit = require("express-rate-limit");
3
+ const tooManyMsg = {
4
+ code: 429,
5
+ status: "TOO_MANY_REQUEST",
6
+ message: "Too Many Requests.",
7
+ };
8
+ let configure = {
9
+ windowMs: (_beech_.defineConfig.server.rateLimit) ? _beech_.defineConfig.server.rateLimit.windowMs : 0,
10
+ standardHeaders: (_beech_.defineConfig.server.rateLimit) ? (_beech_.defineConfig.server.rateLimit.standardHeaders || "draft-7") : "draft-7",
11
+ legacyHeaders: (_beech_.defineConfig.server.rateLimit) ? (_beech_.defineConfig.server.rateLimit.legacyHeaders || false) : false,
12
+ message: (_beech_.defineConfig.server.rateLimit) ? (_beech_.defineConfig.server.rateLimit.message || tooManyMsg) : tooManyMsg,
13
+ };
14
+ configure = { ...configure, ..._beech_.defineConfig.server.rateLimit };
15
+ const Limiter = rateLimit(configure);
16
+
17
+ module.exports = { Limiter, rateLimit };
@@ -0,0 +1,2 @@
1
+ const { slowDown } = require("express-slow-down");
2
+ module.exports = { slowDown };
@@ -0,0 +1,6 @@
1
+ const { whitelist, sign } = require("./origin/whitelist/cors");
2
+ const { avg } = require("./origin/guard/advance");
3
+ const { Limiter } = require("./express/rateLimit");
4
+ const { Duplicater } = require("./express/duplicateRequest");
5
+
6
+ module.exports = { whitelist, sign, Limiter, Duplicater, avg };
@@ -0,0 +1,74 @@
1
+ const fs = require("fs");
2
+ const passport_config_file = "/passport.config.js";
3
+ const { avgDeHashIt } = require(__dirname + "/../../../helpers/math");
4
+ const moment = require("moment");
5
+
6
+ function avg(req, res, next) {
7
+ // check passport file ?
8
+ const checkPassport = new Promise((resolve) => {
9
+ if (fs.existsSync(appRoot + "/passport.config.js")) {
10
+ resolve([true, require(appRoot + passport_config_file)]);
11
+ } else {
12
+ resolve([false, null]);
13
+ }
14
+ });
15
+ // promise all
16
+ Promise.all([checkPassport]).then((final) => {
17
+ let item = final[0];
18
+ let passport_config = item[1];
19
+ /**
20
+ * item[0] : Boolean = passport file found.
21
+ * item[1] : Object = passport object.
22
+ */
23
+ if(item[0]) {
24
+ if ((passport_config.model.guard.advanced_guard) ? passport_config.model.guard.advanced_guard.allow : false) {
25
+ let advanced_guard_entity = req.headers[passport_config.model.guard.advanced_guard.entity || "timing"];
26
+ if (advanced_guard_entity) {
27
+ if(advanced_guard_entity.length > 60) {
28
+ //logic check advanced guard
29
+ avgDeHashIt(advanced_guard_entity, (err, unixTime) => {
30
+ if (err) {
31
+ res.status(502).json({ code: 502, status: "BAD_GATEWAY", message: String(err) });
32
+ return;
33
+ }
34
+ // prepare date & check it.
35
+ let unixTimeNow = moment(new Date()).unix();
36
+ let unixTiming = moment(new Date(unixTime * 1000)).add((passport_config.model.guard.advanced_guard.time_expired.minutes || 0), "minutes").add((passport_config.model.guard.advanced_guard.time_expired.seconds || 0), "seconds").unix();
37
+ if((String(unixTimeNow).length == String(unixTiming).length) && unixTimeNow < unixTiming) {
38
+ next();
39
+ } else {
40
+ res.status(408).json({ code: 408 , status: "REQUEST_TIMEOUT", message: "Request Timeout." });
41
+ }
42
+ });
43
+ } else {
44
+ res.status(400).json({
45
+ code: 400,
46
+ status: 'BAD_REQUEST',
47
+ message: "Bad request.",
48
+ info: {
49
+ status: "BAD_VALUE",
50
+ message: "Bad with wrong Advance guard."
51
+ },
52
+ });
53
+ }
54
+ } else {
55
+ res.status(400).json({
56
+ code: 400,
57
+ status: 'BAD_REQUEST',
58
+ message: "Bad request.",
59
+ info: {
60
+ status: "BAD_ENTITY",
61
+ message: "Bad Advanced guard Entity."
62
+ },
63
+ });
64
+ }
65
+ } else {
66
+ next();
67
+ }
68
+ } else {
69
+ next();
70
+ }
71
+ });
72
+ }
73
+
74
+ module.exports = { avg };
@@ -1,5 +1,5 @@
1
1
  const fs = require("fs");
2
- const appRoot = require("app-root-path");
2
+
3
3
  function whitelist(cb) {
4
4
  var whitelists = [];
5
5
  const getWhitelists = new Promise((resolve) => {
@@ -37,8 +37,8 @@ function sign(req, res, whitelist, originSensitive, cb) {
37
37
  const origin = req.headers.origin;
38
38
  let doYouSignSomeOrigin = false;
39
39
  //var host = req.get("host");
40
- console.log("Request from origin:", origin || "localhost");
41
-
40
+ console.log(`[${_requestTime_}] : From origin: ${origin || 'http://localhost'}`);
41
+ // check whitelist length ?
42
42
  if (whitelist.length > 0) {
43
43
  whitelist.forEach((val, k) => {
44
44
  if (origin) {
@@ -53,21 +53,19 @@ function sign(req, res, whitelist, originSensitive, cb) {
53
53
  }
54
54
  if (whitelist.length == k + 1) {
55
55
  if (!doYouSignSomeOrigin) {
56
- res.setHeader(
57
- "Access-Control-Allow-Origin",
58
- "http://localhost:" + _config_.main_config.app_port
59
- );
56
+ setLocalHeader(res);
60
57
  }
61
58
  }
62
59
  });
63
60
  } else {
64
- res.setHeader("Access-Control-Allow-Origin", origin);
61
+ if(origin) {
62
+ res.setHeader("Access-Control-Allow-Origin", origin); // add origin when not sign origin: []|["*"]
63
+ } else {
64
+ setLocalHeader(res);
65
+ }
65
66
  }
66
67
  // Request methods you wish to allow
67
- res.setHeader(
68
- "Access-Control-Allow-Methods",
69
- "GET, POST, OPTIONS, PUT, PATCH, DELETE"
70
- );
68
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE");
71
69
  // Request headers you wish to allow
72
70
  res.setHeader(
73
71
  "Access-Control-Allow-Headers",
@@ -88,4 +86,9 @@ function sign(req, res, whitelist, originSensitive, cb) {
88
86
  }
89
87
  }
90
88
 
89
+ function setLocalHeader(res) {
90
+ res.setHeader("Access-Control-Allow-Origin", "http://localhost:" + _config_.main_config.app_port);
91
+ res.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:" + _config_.main_config.app_port);
92
+ }
93
+
91
94
  module.exports = { whitelist, sign };