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.

Potentially problematic release.


This version of beech-api might be problematic. Click here for more details.

@@ -2,6 +2,8 @@
2
2
  const logUpdate = require("log-update");
3
3
  const inquirer = require('inquirer');
4
4
  const walk = require("walk");
5
+ const recast = require("recast");
6
+ const { builders } = require("ast-types");
5
7
  const { connectForGenerateModel } = require("../databases/test");
6
8
 
7
9
  class Generator {
@@ -179,6 +181,24 @@ class Generator {
179
181
  resolve("\n Faltal  commnad it's not available.");
180
182
  }
181
183
  }
184
+ } else if (this.option == 'update') {
185
+ if(this.argument == 'model') {
186
+ if(this.special) {
187
+ if (!this.special) {
188
+ resolve("\n Warning  Please specify model name to update.");
189
+ } else {
190
+ this.updateModel()
191
+ .then(res => resolve(res))
192
+ .catch(err => reject(err));
193
+ }
194
+ } else {
195
+ resolve("\n Warning  Please specify model name to update.");
196
+ }
197
+ } else if(this.argument != 'model') {
198
+ resolve("\n Faltal  commnad it's not available.");
199
+ } else {
200
+ resolve("\n Warning  Please specify what you want to update.");
201
+ }
182
202
  } else if (this.option == 'passport') {
183
203
  if (this.argument == "init") {
184
204
  this.makePassportInit()
@@ -451,6 +471,150 @@ class Generator {
451
471
  });
452
472
  }
453
473
 
474
+ updateModel() {
475
+ return new Promise((resolve, reject) => {
476
+ try {
477
+ this.fs.readFile("./global.config.js", 'utf8', (err, globalData) => {
478
+ if (err) return resolve("\n Faltal  Can't read `global.config.js` file.");
479
+ this.fs.readFile("./app.config.js", 'utf8', (appErr, appData) => {
480
+ if (appErr) return resolve("\n Faltal  Can't read `app.config.js` file.");
481
+ const appConfig = eval(appData);
482
+ const modelName = this.special; // <table_name>
483
+ const modelFileName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
484
+ const modelPath = `./src/models/${modelFileName}.js`;
485
+ if (!this.fs.existsSync(modelPath)) {
486
+ return resolve(`\n Warning  Model file \`${modelFileName}\` not found.`);
487
+ }
488
+ // Choose connection for new schema
489
+ inquirer.prompt([{
490
+ type: "list",
491
+ name: "selectDbConnect",
492
+ message: " [93mSelect database connection to sync schema: [0m",
493
+ choices: appConfig.database_config.map(e => e.name),
494
+ }]).then(dbSelected => {
495
+ const { connectForGenerateModel } = require("../databases/test");
496
+ // pull new schema from database
497
+ connectForGenerateModel(dbSelected.selectDbConnect, modelName, appConfig.database_config, (dbErr, tableSchema, tableName) => {
498
+ if (dbErr) return reject(dbErr);
499
+ // read current model file for AST
500
+ this.fs.readFile(modelPath, 'utf8', (readErr, code) => {
501
+ if (readErr) return reject(readErr);
502
+ const ast = recast.parse(code);
503
+ let isUpdated = false;
504
+ // Function for map raw database type to Sequelize DataTypes AST Node
505
+ const getDataTypeNode = (rawType) => {
506
+ const type = rawType.toUpperCase();
507
+ if (type.includes('INT')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('INTEGER'));
508
+ if (type.includes('BIGINT')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('BIGINT'));
509
+ if (type.includes('FLOAT')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('FLOAT'));
510
+ if (type.includes('DOUBLE')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('DOUBLE'));
511
+ if (type.includes('DECIMAL')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('DECIMAL'));
512
+ if (type.includes('BOOLEAN') || type === 'TINYINT(1)') return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('BOOLEAN'));
513
+ if (type.includes('CHAR') || type.includes('VARCHAR')) {
514
+ const match = type.match(/\((\d+)\)/);
515
+ const length = match ? match[1] : '255';
516
+ return builders.callExpression(
517
+ builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('STRING')),
518
+ [builders.literal(parseInt(length))]
519
+ );
520
+ }
521
+ if (type.includes('TEXT')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('TEXT'));
522
+ if (type.includes('DATE')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('DATE'));
523
+ if (type.includes('TIME')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('TIME'));
524
+ if (type.includes('JSON')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('JSON'));
525
+ if (type.includes('UUID')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('UUID'));
526
+ if (type.includes('BLOB')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('BLOB'));
527
+ if (type.includes('ENUM')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('ENUM'));
528
+ if (type.includes('GEOMETRY')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('GEOMETRY'));
529
+ return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('STRING'));
530
+ };
531
+ // recast.visit for find position of Schema(...).define() and update fields
532
+ recast.visit(ast, {
533
+ visitCallExpression(path) {
534
+ const node = path.node;
535
+ if (node.callee.property && node.callee.property.name === 'define' && node.arguments[1] && node.arguments[1].type === 'ObjectExpression') {
536
+ const fieldsObject = node.arguments[1];
537
+ const newFieldNames = Object.keys(tableSchema);
538
+ const updatedProperties = [];
539
+ newFieldNames.forEach(dbFieldName => {
540
+ const dbFieldData = tableSchema[dbFieldName];
541
+ let existingProperty = fieldsObject.properties.find(p => {
542
+ const propName = p.key.name || p.key.value;
543
+ if (propName === dbFieldName) return true;
544
+ if (p.value && p.value.properties) {
545
+ const hasFieldMapping = p.value.properties.find(
546
+ innerP => (innerP.key.name === 'field' || innerP.key.value === 'field') &&
547
+ (innerP.value.value === dbFieldName)
548
+ );
549
+ if (hasFieldMapping) return true;
550
+ }
551
+ return false;
552
+ });
553
+ const latestFieldProps = [];
554
+ // Prepare properties from Table DB (Type, PK, AI, Default, etc.)
555
+ latestFieldProps.push(builders.property('init', builders.identifier('type'), getDataTypeNode(dbFieldData.type)));
556
+ latestFieldProps.push(builders.property('init', builders.identifier('allowNull'), builders.literal(dbFieldData.allowNull === 'YES' || dbFieldData.allowNull === true)));
557
+ if (dbFieldData.primaryKey) latestFieldProps.push(builders.property('init', builders.identifier('primaryKey'), builders.literal(true)));
558
+ if (dbFieldData.autoIncrement) latestFieldProps.push(builders.property('init', builders.identifier('autoIncrement'), builders.literal(true)));
559
+ if (dbFieldData.unique) latestFieldProps.push(builders.property('init', builders.identifier('unique'), builders.literal(true)));
560
+ if(this.extra != '--no-comment') {
561
+ if (dbFieldData.comment) latestFieldProps.push(builders.property('init', builders.identifier('comment'), builders.literal(dbFieldData.comment)));
562
+ }
563
+ if (dbFieldData.defaultValue !== null && dbFieldData.defaultValue !== undefined) {
564
+ const dVal = String(dbFieldData.defaultValue).toUpperCase();
565
+ const defaultNode = (dVal === 'CURRENT_TIMESTAMP' || dVal === 'NOW()')
566
+ ? builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('NOW'))
567
+ : builders.literal(dbFieldData.defaultValue);
568
+ latestFieldProps.push(builders.property('init', builders.identifier('defaultValue'), defaultNode));
569
+ }
570
+ if (!existingProperty) {
571
+ existingProperty = builders.property('init', builders.identifier(dbFieldName), builders.objectExpression(latestFieldProps));
572
+ isUpdated = true;
573
+ } else {
574
+ const currentProps = existingProperty.value.properties;
575
+ latestFieldProps.forEach(newProp => {
576
+ const oldProp = currentProps.find(p => p.key.name === newProp.key.name);
577
+ if (!oldProp || recast.print(oldProp.value).code !== recast.print(newProp.value).code) {
578
+ isUpdated = true;
579
+ if (!oldProp) currentProps.push(newProp); else oldProp.value = newProp.value;
580
+ }
581
+ });
582
+ const latestPropNames = latestFieldProps.map(p => p.key.name);
583
+ latestPropNames.push('field');
584
+ existingProperty.value.properties = currentProps.filter(p => latestPropNames.includes(p.key.name));
585
+ }
586
+ updatedProperties.push(existingProperty);
587
+ });
588
+ const currentOrder = fieldsObject.properties.map(p => p.key.name || p.key.value).join(',');
589
+ const newOrder = updatedProperties.map(p => p.key.name || p.key.value).join(',');
590
+ if (currentOrder !== newOrder || fieldsObject.properties.length !== updatedProperties.length) isUpdated = true;
591
+ fieldsObject.properties = updatedProperties;
592
+ }
593
+ return false;
594
+ }
595
+ });
596
+ // Write back updated AST, Shout updated
597
+ if (isUpdated) {
598
+ // Write updated AST back to file
599
+ const output = recast.print(ast).code;
600
+ this.fs.writeFile(modelPath, output, 'utf8', (writeErr) => {
601
+ if (writeErr) return reject(writeErr);
602
+ resolve(`\n Updated  The model \`${modelFileName}\` has been updated via AST.`);
603
+ });
604
+ } else {
605
+ resolve(`\n Warning  No new fields found to update for \`${modelFileName}\`.`);
606
+ }
607
+ });
608
+ });
609
+ });
610
+ });
611
+ });
612
+ } catch (error) {
613
+ reject(error);
614
+ }
615
+ });
616
+ }
617
+
454
618
  generateModel(tmpModelsPath, dbSelected, appBuf2eval, pool_base) {
455
619
  return new Promise((resolve, reject) => {
456
620
  try {
@@ -611,21 +775,19 @@ class Generator {
611
775
  // Check is primary key
612
776
  if (props.primaryKey) lines.push(` primaryKey: true,`);
613
777
  if (props.autoIncrement) lines.push(` autoIncrement: true,`);
614
- //if (props.comment) lines.push(` comment: '${props.comment}',`);
615
-
778
+ // Check <extra> for comment, because some database have extra comment in field but it's not comment for field, so we need to check it first before assign to comment
779
+ if(this.extra != '--no-comment') {
780
+ if (props.comment) lines.push(` comment: "${props.comment}",`);
781
+ }
616
782
  // Handle defaultValue
617
783
  if (props.defaultValue !== null && props.defaultValue !== undefined) {
618
784
  const defaultVal = String(props.defaultValue).toUpperCase();
619
- if (
620
- defaultVal === 'CURRENT_TIMESTAMP' ||
621
- defaultVal === 'NOW()' ||
622
- defaultVal.includes('CURRENT_TIMESTAMP')
623
- ) {
785
+ if (defaultVal === 'CURRENT_TIMESTAMP' || defaultVal === 'NOW()' || defaultVal.includes('CURRENT_TIMESTAMP')) {
624
786
  lines.push(` defaultValue: DataTypes.NOW,`);
625
787
  } else if (defaultVal === 'UUID()' || defaultVal === 'uuid()' || defaultVal === 'UUID') {
626
788
  lines.push(` defaultValue: DataTypes.UUIDV4,`);
627
789
  } else if (typeof props.defaultValue === 'string') {
628
- lines.push(` defaultValue: '${props.defaultValue}',`);
790
+ lines.push(` defaultValue: "${props.defaultValue}",`);
629
791
  } else {
630
792
  lines.push(` defaultValue: ${props.defaultValue},`);
631
793
  }
@@ -15,7 +15,6 @@ _app_.use(helmet());
15
15
  const cors = require("cors");
16
16
  global.endpoint = _express_.Router();
17
17
  const cookieParser = require("cookie-parser");
18
- const bodyParser = require("body-parser");
19
18
  const methodOverride = require("method-override");
20
19
  const expressSession = require("express-session");
21
20
  const expressValidator = require("express-validator");
@@ -27,10 +26,86 @@ const _beech_ = require(appRoot + "/beech.config.js").defineConfig;
27
26
  global._publicPath_ = _beech_.base;
28
27
  const mySqlDbConnect = require("./databases/mysql");
29
28
  const SequelizeDbConnect = require("./databases/sequelize");
30
- // Rate Request middleware
31
- const { Limiter, Duplicater } = require("./middleware/index");
32
- endpoint.use(Limiter);
33
- endpoint.use(Duplicater);
29
+ // Set limit payload for request body & multipart/form-data (multer)
30
+ const multer = require("multer");
31
+ const uploadAllowMethod = _beech_?.payload?.file?.uploadAllowMethod || ["POST", "PATCH", "PUT"];
32
+ const allowedTypes = _beech_?.payload?.file?.allowedTypes || []; // default: no allowed type
33
+ const fileLimitSize = _beech_?.payload?.file?.limit || 5 * 1024 * 1024; // 5MB
34
+ const uploadStrategy = multer({
35
+ limits: { fileSize: fileLimitSize },
36
+ fileFilter: (req, file, cb) => {
37
+ if (allowedTypes.length === 0) {
38
+ cb(new Error("INVALID_FILE_TYPE_ALLOW"), false); // Allow all file types if no allowed types specified
39
+ } else if (allowedTypes.includes(file.mimetype)) {
40
+ cb(null, true);
41
+ } else {
42
+ cb(new Error("INVALID_FILE_TYPE"), false);
43
+ }
44
+ }
45
+ }).any();
46
+ const jsonLimitSize = _beech_?.payload?.json?.limit || "100KB"; // json payload
47
+ const urlencodedLimitSize = _beech_?.payload?.urlencoded?.limit || "100KB"; // urlencoded payload (multipart/form-data)
48
+ const urlencodedExtended = !!(_beech_?.payload?.urlencoded?.extended ?? true);
49
+ _app_.use(_express_.urlencoded({ limit: urlencodedLimitSize, extended: urlencodedExtended })); // application/x-www-form-urlencoded payload
50
+ _app_.use(_express_.json({ limit: jsonLimitSize }));
51
+ _app_.use((req, res, next) => {
52
+ const isUploadMethod = uploadAllowMethod.includes(req.method);
53
+ const isMultipart = req.headers["content-type"]?.includes("multipart/form-data");
54
+ // Handle file upload for allowed methods
55
+ if (isUploadMethod) {
56
+ return uploadStrategy(req, res, (err) => {
57
+ if (err) return next(err);
58
+ next();
59
+ });
60
+ }
61
+ // Handle method not allowed for file upload
62
+ if (!isUploadMethod && isMultipart) {
63
+ return res.status(405).json({
64
+ code: 405,
65
+ status: "METHOD_NOT_ALLOWED_FOR_UPLOAD",
66
+ message: _config_.main_config?.dev ? `File upload is not allowed for ${req.method} method.` : "Method Not Allowed for file upload.",
67
+ });
68
+ }
69
+ // next to next middleware
70
+ next();
71
+ });
72
+ _app_.use((err, req, res, next) => {
73
+ // Handle payload too large error for JSON and URL-encoded
74
+ if (err.type === "entity.too.large") {
75
+ const isJson = req.headers["content-type"]?.includes("application/json");
76
+ const limitUsed = isJson ? jsonLimitSize : urlencodedLimitSize;
77
+ return res.status(413).json({
78
+ code: 413,
79
+ status: "PAYLOAD_TOO_LARGE",
80
+ message:_config_.main_config?.dev ? `${isJson ? 'JSON' : 'Form data'} too large, Max limit is ${limitUsed}` : "Payload Too Large.",
81
+ });
82
+ }
83
+ if (err.message === "INVALID_FILE_TYPE_ALLOW") {
84
+ return res.status(400).json({
85
+ code: 400,
86
+ status: "INVALID_FILE_TYPE_ALLOW",
87
+ message: "Invalid file type, No file types allowed.",
88
+ });
89
+ }
90
+ // Handle invalid file type error from multer
91
+ if (err.message === "INVALID_FILE_TYPE") {
92
+ return res.status(400).json({
93
+ code: 400,
94
+ status: "INVALID_FILE_TYPE",
95
+ message: _config_.main_config?.dev ? `Invalid file type, Allowed: ${allowedTypes.join(', ')}` : "Invalid file type.",
96
+ });
97
+ }
98
+ // Handle multer file size limit error
99
+ if (err.code === "LIMIT_FILE_SIZE") {
100
+ return res.status(413).json({
101
+ code: 413,
102
+ status: "PAYLOAD_TOO_LARGE",
103
+ message: _config_.main_config?.dev ? `File size too large, Max limit is ${fileLimitSize / 1024 / 1024}MB` : "Payload Too Large.",
104
+ });
105
+ }
106
+ // next to general error handler
107
+ next(err);
108
+ });
34
109
  // Database test
35
110
  const {
36
111
  testConnectInProcess,
@@ -57,8 +132,6 @@ _app_.use((req, res, next) => {
57
132
  });
58
133
  });
59
134
  // View engine
60
- _app_.use(bodyParser.json());
61
- _app_.use(bodyParser.urlencoded({ extended: true }));
62
135
  _app_.use(methodOverride());
63
136
  _app_.use(cookieParser());
64
137
  _app_.use(expressSession({
@@ -1,12 +1,16 @@
1
1
  const _beech_ = require(appRoot + "/beech.config.js");
2
2
  const { duplicateRequest } = require("express-duplicate-request");
3
- const nextDuplicater = (req, res, next) => {
4
- next();
5
- };
6
- let configure = {
3
+ const nextDuplicater = (req, res, next) => next();
4
+ const defaultConfigure = {
7
5
  expiration: _beech_.defineConfig.server.duplicateRequest ? _beech_.defineConfig.server.duplicateRequest.expiration : 0,
8
6
  };
9
- configure = { ...configure, ..._beech_.defineConfig.server.duplicateRequest };
10
- const Duplicater = configure.expiration ? duplicateRequest(configure) : nextDuplicater;
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
+ };
11
15
 
12
16
  module.exports = { Duplicater, duplicateRequest };
@@ -26,9 +26,10 @@ const checkRoleMiddleware = (options) => {
26
26
  return next();
27
27
  } else {
28
28
  const allowed = options.some(rule => {
29
- Object.entries(rule).every(([key, condition]) => {
29
+ return Object.entries(rule).every(([key, condition]) => {
30
+ //console.log('----matchCondition(user?.[key], condition, user)-->>', matchCondition(user?.[key], condition, user));
30
31
  return matchCondition(user?.[key], condition, user);
31
- })
32
+ });
32
33
  });
33
34
  if (allowed) {
34
35
  return next();
@@ -63,7 +64,6 @@ const operators = {
63
64
  };
64
65
 
65
66
  const matchCondition = (userValue, condition, user) => {
66
- // primitive -> eq
67
67
  if (typeof condition !== 'object' || condition instanceof RegExp || Array.isArray(condition)) {
68
68
  return Array.isArray(condition)
69
69
  ? operators.$in(userValue, condition)
@@ -11,7 +11,19 @@ let configure = {
11
11
  legacyHeaders: (_beech_.defineConfig.server.rateLimit) ? (_beech_.defineConfig.server.rateLimit.legacyHeaders || false) : false,
12
12
  message: (_beech_.defineConfig.server.rateLimit) ? (_beech_.defineConfig.server.rateLimit.message || tooManyMsg) : tooManyMsg,
13
13
  };
14
- configure = { ...configure, ..._beech_.defineConfig.server.rateLimit };
15
- const Limiter = rateLimit(configure);
14
+ configure = {
15
+ ..._beech_.defineConfig.server.rateLimit, // Override default configure with user configure.
16
+ ...configure,
17
+ keyGenerator: (req) => `${req.ip}:${req.params.hash}${req.params['0']}`,
18
+ };
19
+ const Limiter = (more_configure = {}) => {
20
+ const middleware = rateLimit({
21
+ ...configure,
22
+ ...more_configure,
23
+ });
24
+ return (req, res, next) => {
25
+ middleware(req, res, next);
26
+ };
27
+ };
16
28
 
17
29
  module.exports = { Limiter, rateLimit };
@@ -3,6 +3,7 @@ const fs = require("fs");
3
3
  const passport_config_file = "/passport.config.js";
4
4
  const auth = require("../auth/Credentials");
5
5
  const { TwoFactor } = require("../helpers/2fa");
6
+ const { Limiter, Duplicater } = require("../middleware");
6
7
 
7
8
  module.exports = {
8
9
  expressStart() {
@@ -142,7 +143,7 @@ module.exports = {
142
143
  // declare authentication endpoint name with publicPath
143
144
  let auth_endpoint = (passport_config.auth_endpoint) ? (passport_config.auth_endpoint[ 0 ] === "/" ? passport_config.auth_endpoint : "/" + passport_config.auth_endpoint) : "/authentication";
144
145
  // authentication endpoints
145
- endpoint.post(auth_endpoint, auth.credentialsGuard, (req, res, next) => {
146
+ endpoint.post(auth_endpoint, Duplicater(), Limiter(), auth.credentialsGuard, (req, res, next) => {
146
147
  passport.authenticate('local', { session: false }, (err, user, opt) => {
147
148
  if (err) {
148
149
  res.status(502).json({ code: 502, status: "BAD_GATEWAY", message: String(err) });
@@ -194,8 +195,49 @@ module.exports = {
194
195
  }
195
196
  })(req, res, next);
196
197
  });
198
+ // refresh token endpoints
199
+ endpoint.post(auth_endpoint + '/refresh', Duplicater(), Limiter(), auth.credentials, (req, res) => {
200
+ const refreshToken = req.headers.authorization.split("Bearer ")[1] || null;
201
+ if (!refreshToken) {
202
+ return res.status(400).json({
203
+ code: 400,
204
+ status: "BAD_REQUEST",
205
+ message: "Bad request.",
206
+ info: {
207
+ status: "BAD_ENTITY",
208
+ message: "Refresh token is required.",
209
+ }
210
+ });
211
+ } else {
212
+ jwt.verify(refreshToken, passport_config.secret, (err, user) => {
213
+ if (err) {
214
+ return res.status(401).json({
215
+ code: 401,
216
+ status: "UNAUTHORIZED",
217
+ message: "Unauthorized user.",
218
+ info: {
219
+ status: "TOKEN_INVALID_ERR",
220
+ message: "Invalid refresh token.",
221
+ }
222
+ });
223
+ } else {
224
+ delete user.iat;
225
+ delete user.exp;
226
+ const newAccessToken = jwt.sign(user, passport_config.secret, {
227
+ expiresIn: passport_config.token_expired
228
+ });
229
+ res.status(200).json({
230
+ code: 200,
231
+ status: "TOKEN_REFRESHED",
232
+ user: user,
233
+ accessToken: newAccessToken,
234
+ });
235
+ }
236
+ });
237
+ }
238
+ });
197
239
  // create auth data endpoints
198
- endpoint.post(auth_endpoint + '/create', auth.credentialsGuard, (req, res) => {
240
+ endpoint.post(auth_endpoint + '/create', Duplicater(), Limiter(), auth.credentialsGuard, (req, res) => {
199
241
  const promise = new Promise((resolve) => {
200
242
  /**
201
243
  *
@@ -249,7 +291,7 @@ module.exports = {
249
291
  });
250
292
  });
251
293
  // patch auth data endpoints
252
- endpoint.patch(auth_endpoint + '/update/:id', auth.credentials, (req, res) => {
294
+ endpoint.patch(auth_endpoint + '/update/:id', Duplicater(), Limiter(), auth.credentials, (req, res) => {
253
295
  const promise = new Promise((resolve) => {
254
296
  if (passport_config.app_key_allow) {
255
297
  if (req.headers.app_key) {
@@ -303,7 +345,7 @@ module.exports = {
303
345
  *
304
346
  */
305
347
  if (passport_config.strategy.google.allow) {
306
- endpoint.get(auth_endpoint + '/google', passport.authenticate('google', {
348
+ endpoint.get(auth_endpoint + '/google', Limiter(), passport.authenticate('google', {
307
349
  scope: [
308
350
  'https://www.googleapis.com/auth/userinfo.email',
309
351
  'https://www.googleapis.com/auth/plus.login'
@@ -311,7 +353,7 @@ module.exports = {
311
353
  }));
312
354
  // google auth callback
313
355
  const googleCallback = (passport_config.strategy.google.callbackURL) ? (passport_config.strategy.google.callbackURL[ 0 ] === "/" ? passport_config.strategy.google.callbackURL : "/" + passport_config.strategy.google.callbackURL) : "/google/callback";
314
- endpoint.get(auth_endpoint + googleCallback, passport.authenticate('google', { failureRedirect: passport_config.strategy.google.failureRedirect, failureMessage: true }), (req, res) => {
356
+ endpoint.get(auth_endpoint + googleCallback, Limiter(), passport.authenticate('google', { failureRedirect: passport_config.strategy.google.failureRedirect, failureMessage: true }), (req, res) => {
315
357
  if (typeof req.user.user !== 'undefined') {
316
358
  // declare user for sign JWT
317
359
  let user = JSON.parse(JSON.stringify(req.user.user));
@@ -360,10 +402,10 @@ module.exports = {
360
402
  *
361
403
  */
362
404
  if (passport_config.strategy.facebook.allow) {
363
- endpoint.get(auth_endpoint + '/facebook', passport.authenticate('facebook', { scope: [ 'email', 'public_profile' ] }));
405
+ endpoint.get(auth_endpoint + '/facebook', Limiter(), passport.authenticate('facebook', { scope: [ 'email', 'public_profile' ] }));
364
406
  // facebook callback
365
407
  const facebookCallback = (passport_config.strategy.facebook.callbackURL) ? (passport_config.strategy.facebook.callbackURL[ 0 ] === "/" ? passport_config.strategy.facebook.callbackURL : "/" + passport_config.strategy.facebook.callbackURL) : "/facebook/callback";
366
- endpoint.get(auth_endpoint + facebookCallback, passport.authenticate('facebook', { failureRedirect: passport_config.strategy.facebook.failureRedirect, failureMessage: true }), (req, res) => {
408
+ endpoint.get(auth_endpoint + facebookCallback, Limiter(), passport.authenticate('facebook', { failureRedirect: passport_config.strategy.facebook.failureRedirect, failureMessage: true }), (req, res) => {
367
409
  if (typeof req.user.user !== 'undefined') {
368
410
  // declare user for sign JWT
369
411
  let user = JSON.parse(JSON.stringify(req.user.user));
@@ -0,0 +1,21 @@
1
+ const current = process.versions.node;
2
+
3
+ const required = {
4
+ min16: "16.20.0",
5
+ min22: "22.18.0",
6
+ };
7
+
8
+ const semver = (a, b) => {
9
+ const pa = a.split(".").map(Number);
10
+ const pb = b.split(".").map(Number);
11
+ for (let i = 0; i < 3; i++) {
12
+ if (pa[i] > pb[i]) return 1;
13
+ if (pa[i] < pb[i]) return -1;
14
+ }
15
+ return 0;
16
+ };
17
+
18
+ if (semver(current, required.min16) < 0 && semver(current, required.min22) < 0) {
19
+ console.error(`\n❌ You are using Node.js ${current}.\n` + `Beech requires Node.js version ${required.min16}+ or ${required.min22}+.\n` + `Please upgrade your Node.js version.\n`);
20
+ process.exit(1);
21
+ }