beech-api 3.8.0 → 3.9.0-beta.9-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 +551 -146
- package/package.json +13 -12
- package/packages/cli/bin/beech-app.js +4 -4
- package/packages/cli/bin/beech-service.js +62 -25
- package/packages/cli/core/auth/Credentials.js +115 -88
- 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 +125 -39
- package/packages/cli/core/generator/_models +3 -26
- package/packages/cli/core/generator/_models_basic +0 -9
- package/packages/cli/core/generator/_package +6 -7
- package/packages/cli/core/generator/_scheduler +16 -6
- package/packages/cli/core/generator/index.js +277 -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 +88 -10
- package/packages/cli/core/middleware/express/duplicateRequest.js +10 -6
- package/packages/cli/core/middleware/express/jwtCheckAllow.js +52 -34
- package/packages/cli/core/middleware/express/rateLimit.js +14 -2
- package/packages/cli/core/middleware/origin/guard/advance.js +5 -4
- package/packages/cli/core/services/http.express.js +49 -9
- package/packages/cli/core/test/check-node.js +21 -0
- package/packages/lib/src/endpoint.js +639 -286
- package/packages/lib/src/schema.js +4 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const logUpdate = require("log-update");
|
|
3
3
|
const inquirer = require('inquirer');
|
|
4
4
|
const walk = require("walk");
|
|
5
|
+
const { connectForGenerateModel } = require("../databases/test");
|
|
5
6
|
|
|
6
7
|
class Generator {
|
|
7
8
|
constructor() {
|
|
@@ -178,6 +179,18 @@ class Generator {
|
|
|
178
179
|
resolve("\n[101m Faltal [0m commnad it's not available.");
|
|
179
180
|
}
|
|
180
181
|
}
|
|
182
|
+
} else if (this.option == 'update') {
|
|
183
|
+
console.log('------------------------------');
|
|
184
|
+
if(this.argument == 'model') {
|
|
185
|
+
// this.special = model name
|
|
186
|
+
// if (!this.special) {
|
|
187
|
+
// resolve("\n [103m [90m Warning [0m [0m Please specify model name to update.");
|
|
188
|
+
// } else {
|
|
189
|
+
// this.updateModel()
|
|
190
|
+
// .then(res => resolve(res))
|
|
191
|
+
// .catch(err => reject(err));
|
|
192
|
+
// }
|
|
193
|
+
}
|
|
181
194
|
} else if (this.option == 'passport') {
|
|
182
195
|
if (this.argument == "init") {
|
|
183
196
|
this.makePassportInit()
|
|
@@ -190,21 +203,42 @@ class Generator {
|
|
|
190
203
|
this.generateKeyConfigFile()
|
|
191
204
|
.then(resGenKey => resolve(resGenKey))
|
|
192
205
|
.catch(err => reject(err));
|
|
193
|
-
} else if (this.option && this.option.slice(0, 5) ==
|
|
206
|
+
} else if (this.option && this.option.slice(0, 5) == "hash:") {
|
|
207
|
+
const { HashIt, Z } = require(__dirname + "/../helpers/math");
|
|
194
208
|
if(this.option.length > 5) {
|
|
195
|
-
const { HashIt, Z } = require(__dirname + "/../helpers/math");
|
|
196
209
|
Z((err, ak) => {
|
|
197
210
|
if(err) {
|
|
198
211
|
logUpdate(err);
|
|
199
212
|
} else {
|
|
200
213
|
let txt = this.option.split(":");
|
|
201
214
|
HashIt(txt, ak, null, (5).toString().length, (hashed) => {
|
|
202
|
-
logUpdate(hashed);
|
|
215
|
+
logUpdate("\n" + hashed);
|
|
203
216
|
});
|
|
204
217
|
}
|
|
205
218
|
});
|
|
206
219
|
} else {
|
|
207
|
-
|
|
220
|
+
inquirer.prompt([ {
|
|
221
|
+
type: "confirm",
|
|
222
|
+
name: "confirmHashNoText",
|
|
223
|
+
message: "[93mNo text to hash, Do you want to continue ?:[0m",
|
|
224
|
+
} ]).then(confirm => {
|
|
225
|
+
if(confirm.confirmHashNoText) {
|
|
226
|
+
Z((err, ak) => {
|
|
227
|
+
if(err) {
|
|
228
|
+
logUpdate(err);
|
|
229
|
+
} else {
|
|
230
|
+
let txt = this.option.split(":");
|
|
231
|
+
HashIt(txt, ak, null, (5).toString().length, (hashed) => {
|
|
232
|
+
logUpdate("\n" + hashed);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
} else {
|
|
237
|
+
// no text to hash say no.
|
|
238
|
+
resolve(": Say no.");
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
208
242
|
}
|
|
209
243
|
} else if (this.option == "skd") {
|
|
210
244
|
if (this.argument == "init") {
|
|
@@ -404,12 +438,12 @@ class Generator {
|
|
|
404
438
|
// check pool_base
|
|
405
439
|
if (pool_base[ 1 ] == "basic") {
|
|
406
440
|
tmpModelsPath += '/_models_basic';
|
|
407
|
-
this.generateModel(tmpModelsPath, dbSelected.selectDbConnect)
|
|
441
|
+
this.generateModel(tmpModelsPath, dbSelected.selectDbConnect, appBuf2eval, pool_base[1])
|
|
408
442
|
.then(console.log)
|
|
409
443
|
.catch(console.log);
|
|
410
444
|
} else if (pool_base[ 1 ] == "sequelize") {
|
|
411
445
|
tmpModelsPath += '/_models';
|
|
412
|
-
this.generateModel(tmpModelsPath, dbSelected.selectDbConnect)
|
|
446
|
+
this.generateModel(tmpModelsPath, dbSelected.selectDbConnect, appBuf2eval, pool_base[1])
|
|
413
447
|
.then(console.log)
|
|
414
448
|
.catch(console.log);
|
|
415
449
|
} else {
|
|
@@ -429,35 +463,182 @@ class Generator {
|
|
|
429
463
|
});
|
|
430
464
|
}
|
|
431
465
|
|
|
432
|
-
|
|
466
|
+
updateModel() {
|
|
467
|
+
return new Promise((resolve, reject) => {
|
|
468
|
+
try {
|
|
469
|
+
// 1. อ่านการตั้งค่าจาก global.config และ app.config เหมือนตอนสร้าง Model
|
|
470
|
+
this.fs.readFile("./global.config.js", 'utf8', (err, globalData) => {
|
|
471
|
+
if (err) return resolve("\n [101m Faltal [0m Can't read `global.config.js` file.");
|
|
472
|
+
|
|
473
|
+
this.fs.readFile("./app.config.js", 'utf8', (appErr, appData) => {
|
|
474
|
+
if (appErr) return resolve("\n [101m Faltal [0m Can't read `app.config.js` file.");
|
|
475
|
+
|
|
476
|
+
let appBuf2eval = eval(appData);
|
|
477
|
+
let modelName = this.special; // <table_name>
|
|
478
|
+
let modelPath = `./src/models/${modelName.charAt(0).toUpperCase() + modelName.slice(1)}.js`;
|
|
479
|
+
|
|
480
|
+
// ตรวจสอบว่ามีไฟล์ Model เดิมอยู่หรือไม่
|
|
481
|
+
if (!this.fs.existsSync(modelPath)) {
|
|
482
|
+
return resolve(`\n [103m [90m Warning [0m [0m Model file \`${modelName}\` not found to update.`);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// ให้ผู้ใช้เลือก Connection ที่ต้องการดึง Schema ใหม่
|
|
486
|
+
inquirer.prompt([{
|
|
487
|
+
type: "list",
|
|
488
|
+
name: "selectDbConnect",
|
|
489
|
+
message: " [93mSelect database connection to sync schema: [0m",
|
|
490
|
+
choices: appBuf2eval.database_config.map(e => e.name),
|
|
491
|
+
}]).then(dbSelected => {
|
|
492
|
+
|
|
493
|
+
// 2. ดึง Schema ใหม่จาก DB
|
|
494
|
+
const { connectForGenerateModel } = require("../databases/test");
|
|
495
|
+
let newModelClassName = modelName.split("_").map(e => e.charAt(0).toUpperCase() + e.slice(1)).join("");
|
|
496
|
+
|
|
497
|
+
connectForGenerateModel(dbSelected.selectDbConnect, modelName, appBuf2eval.database_config, (err, tableSchema, tableName) => {
|
|
498
|
+
if (err) return reject(err);
|
|
499
|
+
|
|
500
|
+
// 3. แปลง Schema เป็น Code String
|
|
501
|
+
this.rawSchemaTable(dbSelected.selectDbConnect, newModelClassName, tableName, tableSchema, (SchemaErr, rawSchema) => {
|
|
502
|
+
if (SchemaErr) return reject(SchemaErr);
|
|
503
|
+
|
|
504
|
+
// 4. อ่านไฟล์เดิมและทำการ Replace เฉพาะส่วน Schema
|
|
505
|
+
this.fs.readFile(modelPath, 'utf8', (readErr, currentContent) => {
|
|
506
|
+
if (readErr) return reject(readErr);
|
|
507
|
+
|
|
508
|
+
// ใช้ Regex เพื่อหาช่วงของ Schema(...).define(...) และเปลี่ยนเฉพาะข้างใน
|
|
509
|
+
// โดยจะหาตั้งแต่ Schema(sql.db).define("table", { จนถึง });
|
|
510
|
+
const regex = /const\s+\w+\s+=\s+Schema\(sql\.\w+\)\.define\([\s\S]*?\}\);/;
|
|
511
|
+
|
|
512
|
+
if (regex.test(currentContent)) {
|
|
513
|
+
let updatedContent = currentContent.replace(regex, rawSchema);
|
|
514
|
+
|
|
515
|
+
this.fs.writeFile(modelPath, updatedContent, 'utf8', (writeErr) => {
|
|
516
|
+
if (writeErr) return reject(writeErr);
|
|
517
|
+
resolve(`\n [102m [90m Passed [0m [0m The model \`${modelName}\` schema has been updated.`);
|
|
518
|
+
});
|
|
519
|
+
} else {
|
|
520
|
+
resolve("\n [101m Faltal [0m Could not find Schema definition pattern in the file.");
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
} catch (error) {
|
|
529
|
+
reject(error);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
generateModel(tmpModelsPath, dbSelected, appBuf2eval, pool_base) {
|
|
433
535
|
return new Promise((resolve, reject) => {
|
|
434
536
|
try {
|
|
537
|
+
// Show generating msg
|
|
538
|
+
const frames = [
|
|
539
|
+
'\n[36m[-] Generating[0m',
|
|
540
|
+
'\n[36m[\\] Generating.[0m',
|
|
541
|
+
'\n[36m[|] Generating..[0m',
|
|
542
|
+
'\n[36m[/] Generating...[0m'
|
|
543
|
+
];
|
|
544
|
+
let i = 0;
|
|
545
|
+
var refreshGenerateIntervalId = null;
|
|
546
|
+
// Save model folder
|
|
435
547
|
let modelPath = './src/models/';
|
|
436
|
-
//
|
|
548
|
+
// Argument join `slash`
|
|
437
549
|
let arg = this.argument.replace(/^\/+|\/+$/g, '');
|
|
438
550
|
arg = arg.split('/');
|
|
439
551
|
let models = arg.pop();
|
|
552
|
+
let oriModelsName = models.slice(0);
|
|
440
553
|
models = models.charAt(0).toUpperCase() + models.slice(1);
|
|
441
554
|
let newModel = models.split("_").map(e => e.charAt(0).toUpperCase() + e.slice(1)).join("");
|
|
442
555
|
let subFolder = arg.join('/');
|
|
443
|
-
// models
|
|
556
|
+
// Declare models
|
|
444
557
|
let fullModels = modelPath + subFolder.concat('/') + models.concat('.js');
|
|
445
558
|
|
|
446
|
-
|
|
559
|
+
/**
|
|
560
|
+
* All properties
|
|
561
|
+
*
|
|
562
|
+
* @tmpModelPath String : path to keep generate model file
|
|
563
|
+
* @dbSelect String : database connection name
|
|
564
|
+
* @appBug2eval Object : database connection object
|
|
565
|
+
* @pool_base Object : pool base connection
|
|
566
|
+
*
|
|
567
|
+
* @i Number : loop count running...
|
|
568
|
+
* @refreshGenerateIntervalId Object : for clear interval id
|
|
569
|
+
*
|
|
570
|
+
* @arg String : argument from typing
|
|
571
|
+
* @models String : model name
|
|
572
|
+
* @oriModelsName String : original model name from typing
|
|
573
|
+
* @newModel String : model first upper case
|
|
574
|
+
* @subFolder String : agument sub folder
|
|
575
|
+
* @fullModels String : full model path and model file name
|
|
576
|
+
*
|
|
577
|
+
*/
|
|
578
|
+
// Check file exists
|
|
447
579
|
if (!this.fs.existsSync(fullModels)) {
|
|
448
|
-
//
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
580
|
+
// Check pool base is basic|sequelize
|
|
581
|
+
if(pool_base == "basic") {
|
|
582
|
+
// Generate basic model
|
|
583
|
+
this.makeFolder(modelPath + subFolder)
|
|
584
|
+
.then(this.copy.bind(this, tmpModelsPath, fullModels))
|
|
585
|
+
.then(this.modelContentReplace.bind(this, fullModels, {
|
|
586
|
+
'modelName': oriModelsName,
|
|
587
|
+
'dbSelected': dbSelected,
|
|
588
|
+
}))
|
|
589
|
+
.then(
|
|
590
|
+
refreshGenerateIntervalId = setInterval(() => {
|
|
591
|
+
const frame = frames[i = ++i % frames.length];
|
|
592
|
+
logUpdate(`${frame}`);
|
|
593
|
+
}, 300)
|
|
594
|
+
)
|
|
595
|
+
.then(generated => {
|
|
596
|
+
logUpdate(generated);
|
|
597
|
+
clearInterval(refreshGenerateIntervalId);
|
|
598
|
+
})
|
|
599
|
+
.catch(err => {
|
|
600
|
+
throw err;
|
|
601
|
+
});
|
|
602
|
+
} else if(pool_base == "sequelize") {
|
|
603
|
+
// Gether table schema
|
|
604
|
+
connectForGenerateModel(dbSelected, oriModelsName, appBuf2eval.database_config, (err, tableSchema, tableName) => {
|
|
605
|
+
if(err) {
|
|
606
|
+
throw logUpdate("\n[101m Faltal [0m", String(err), "\n");
|
|
607
|
+
} else {
|
|
608
|
+
// Raw model schema
|
|
609
|
+
this.rawSchemaTable(dbSelected, newModel, tableName, tableSchema, (SchemaErr, rawSchema) => {
|
|
610
|
+
if(err) {
|
|
611
|
+
throw logUpdate("\n[101m Faltal [0m RAW Schema ERR:", String(SchemaErr), "\n");
|
|
612
|
+
} else {
|
|
613
|
+
// Generate sequelize model
|
|
614
|
+
this.makeFolder(modelPath + subFolder)
|
|
615
|
+
.then(this.copy.bind(this, tmpModelsPath, fullModels))
|
|
616
|
+
.then(this.modelContentReplace.bind(this, fullModels, {
|
|
617
|
+
'modelName': oriModelsName,
|
|
618
|
+
'modelNameUppercase': newModel,
|
|
619
|
+
'modelStructure': rawSchema,
|
|
620
|
+
}))
|
|
621
|
+
.then(
|
|
622
|
+
refreshGenerateIntervalId = setInterval(() => {
|
|
623
|
+
const frame = frames[i = ++i % frames.length];
|
|
624
|
+
logUpdate(`${frame}`);
|
|
625
|
+
}, 300)
|
|
626
|
+
)
|
|
627
|
+
.then(generated => {
|
|
628
|
+
logUpdate(generated);
|
|
629
|
+
clearInterval(refreshGenerateIntervalId);
|
|
630
|
+
})
|
|
631
|
+
.catch(err => {
|
|
632
|
+
throw err;
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
}
|
|
460
637
|
});
|
|
638
|
+
} else {
|
|
639
|
+
// Fallback (When Accident)
|
|
640
|
+
resolve("\n[101m Faltal [0m The pool_base in `global.config.js` file does not match the specific.");
|
|
641
|
+
}
|
|
461
642
|
} else {
|
|
462
643
|
resolve("\n[103m[90m Warning [0m[0m The model `" + models + "` it's duplicated.");
|
|
463
644
|
}
|
|
@@ -467,6 +648,78 @@ class Generator {
|
|
|
467
648
|
});
|
|
468
649
|
}
|
|
469
650
|
|
|
651
|
+
rawSchemaTable(dbNameSelected, newModelName, tableName, modelSchema, cb) {
|
|
652
|
+
try {
|
|
653
|
+
// Function map type
|
|
654
|
+
const mapToSequelizeType = (rawType) => {
|
|
655
|
+
const type = rawType.toUpperCase();
|
|
656
|
+
// Assign all conditions
|
|
657
|
+
if (type.includes('INT')) return 'DataTypes.INTEGER';
|
|
658
|
+
if (type.includes('BIGINT')) return 'DataTypes.BIGINT';
|
|
659
|
+
if (type.includes('FLOAT')) return 'DataTypes.FLOAT';
|
|
660
|
+
if (type.includes('DOUBLE')) return 'DataTypes.DOUBLE';
|
|
661
|
+
if (type.includes('DECIMAL')) return 'DataTypes.DECIMAL';
|
|
662
|
+
if (type.includes('BOOLEAN') || type === 'TINYINT(1)') return 'DataTypes.BOOLEAN';
|
|
663
|
+
if (type.includes('CHAR')) {
|
|
664
|
+
const match = type.match(/\((\d+)\)/);
|
|
665
|
+
const length = match ? match[1] : '255';
|
|
666
|
+
return `DataTypes.STRING(${length})`;
|
|
667
|
+
}
|
|
668
|
+
if (type.includes('VARCHAR')) {
|
|
669
|
+
const match = type.match(/\((\d+)\)/);
|
|
670
|
+
const length = match ? match[1] : '255';
|
|
671
|
+
return `DataTypes.STRING(${length})`;
|
|
672
|
+
}
|
|
673
|
+
if (type.includes('TEXT')) return 'DataTypes.TEXT';
|
|
674
|
+
if (type.includes('DATE')) return 'DataTypes.DATE';
|
|
675
|
+
if (type.includes('TIME')) return 'DataTypes.TIME';
|
|
676
|
+
if (type.includes('JSON')) return 'DataTypes.JSON';
|
|
677
|
+
if (type.includes('UUID')) return 'DataTypes.UUID';
|
|
678
|
+
if (type.includes('BLOB')) return 'DataTypes.BLOB';
|
|
679
|
+
if (type.includes('ENUM')) return 'DataTypes.ENUM'; // Needs manual values
|
|
680
|
+
if (type.includes('GEOMETRY')) return 'DataTypes.GEOMETRY';
|
|
681
|
+
// Fallback
|
|
682
|
+
return `DataTypes.STRING`;
|
|
683
|
+
};
|
|
684
|
+
// Looping key fields
|
|
685
|
+
const fields = Object.entries(modelSchema).map(([name, props]) => {
|
|
686
|
+
// Declare line
|
|
687
|
+
const lines = [];
|
|
688
|
+
// Push line and assign space for beautiful
|
|
689
|
+
lines.push(` type: ${mapToSequelizeType(props.type)},`);
|
|
690
|
+
lines.push(` allowNull: ${props.allowNull},`);
|
|
691
|
+
// Check is primary key
|
|
692
|
+
if (props.primaryKey) lines.push(` primaryKey: true,`);
|
|
693
|
+
if (props.autoIncrement) lines.push(` autoIncrement: true,`);
|
|
694
|
+
//if (props.comment) lines.push(` comment: '${props.comment}',`);
|
|
695
|
+
|
|
696
|
+
// Handle defaultValue
|
|
697
|
+
if (props.defaultValue !== null && props.defaultValue !== undefined) {
|
|
698
|
+
const defaultVal = String(props.defaultValue).toUpperCase();
|
|
699
|
+
if (
|
|
700
|
+
defaultVal === 'CURRENT_TIMESTAMP' ||
|
|
701
|
+
defaultVal === 'NOW()' ||
|
|
702
|
+
defaultVal.includes('CURRENT_TIMESTAMP')
|
|
703
|
+
) {
|
|
704
|
+
lines.push(` defaultValue: DataTypes.NOW,`);
|
|
705
|
+
} else if (defaultVal === 'UUID()' || defaultVal === 'uuid()' || defaultVal === 'UUID') {
|
|
706
|
+
lines.push(` defaultValue: DataTypes.UUIDV4,`);
|
|
707
|
+
} else if (typeof props.defaultValue === 'string') {
|
|
708
|
+
lines.push(` defaultValue: '${props.defaultValue}',`);
|
|
709
|
+
} else {
|
|
710
|
+
lines.push(` defaultValue: ${props.defaultValue},`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
// Finally
|
|
714
|
+
return ` ${name}: {\n${lines.join('\n')}\n }`;
|
|
715
|
+
});
|
|
716
|
+
// Callback
|
|
717
|
+
cb(null, `const ${newModelName} = Schema(sql.${dbNameSelected}).define("${tableName}", {\n${fields.join(',\n')}\n});`);
|
|
718
|
+
} catch (error) {
|
|
719
|
+
cb(error, null);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
470
723
|
makeHelper() {
|
|
471
724
|
return new Promise((resolve, reject) => {
|
|
472
725
|
try {
|
|
@@ -632,6 +885,7 @@ class Generator {
|
|
|
632
885
|
text = text.replace(new RegExp('{{dbSelected}}', 'g'), textCondition.dbSelected);
|
|
633
886
|
// check add model name text uppercase
|
|
634
887
|
if (Object.keys(textCondition).length > 1) {
|
|
888
|
+
text = text.replace(new RegExp('{{modelStructure}}', 'g'), textCondition.modelStructure);
|
|
635
889
|
text = text.replace(new RegExp('{{modelNameUppercase}}', 'g'), textCondition.modelNameUppercase);
|
|
636
890
|
}
|
|
637
891
|
// writing the file
|
|
@@ -644,7 +898,7 @@ class Generator {
|
|
|
644
898
|
});
|
|
645
899
|
}
|
|
646
900
|
})
|
|
647
|
-
},
|
|
901
|
+
}, 2000);
|
|
648
902
|
} catch (error) {
|
|
649
903
|
reject(error);
|
|
650
904
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
1
2
|
const { FindOne } = require("../../../lib/src/user");
|
|
2
3
|
const { findPassportPk } = require("./poolEntity");
|
|
4
|
+
const passport_config_file = appRoot + "/passport.config.js";
|
|
5
|
+
const md5 = require("md5");
|
|
6
|
+
const secret = require("../../../lib/src/salt").salt;
|
|
3
7
|
|
|
4
8
|
function TwoFactor(user, reqBody, guard_field, cb) {
|
|
5
9
|
try {
|
|
@@ -8,6 +12,22 @@ function TwoFactor(user, reqBody, guard_field, cb) {
|
|
|
8
12
|
cb(true, err);
|
|
9
13
|
} else {
|
|
10
14
|
if(userId.length) {
|
|
15
|
+
if (fs.existsSync(passport_config_file)) {
|
|
16
|
+
passport_config = require(passport_config_file);
|
|
17
|
+
} else {
|
|
18
|
+
cb(true, {
|
|
19
|
+
code: 500,
|
|
20
|
+
status: "INTERNAL_SERVER_ERR",
|
|
21
|
+
error: {
|
|
22
|
+
code: 404,
|
|
23
|
+
status: "ERROR_FILE_NOT_EXISTS",
|
|
24
|
+
message: "The file passport.config.js not exists!",
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
let usrField = passport_config.model.username_field || "username";
|
|
30
|
+
let pwdField = passport_config.model.password_field || "password"
|
|
11
31
|
// filter without base user, pass
|
|
12
32
|
let without_base = Object.keys(reqBody).map((k) => {
|
|
13
33
|
return guard_field.filter((e) => e == k)[0];
|
|
@@ -19,7 +39,8 @@ function TwoFactor(user, reqBody, guard_field, cb) {
|
|
|
19
39
|
// check length match ?
|
|
20
40
|
if(x.length == guard_field.length) {
|
|
21
41
|
let z = {};
|
|
22
|
-
z[
|
|
42
|
+
z[usrField] = reqBody[usrField];
|
|
43
|
+
z[pwdField] = md5(reqBody[pwdField] + secret);
|
|
23
44
|
x.map((guard) => {
|
|
24
45
|
z[guard] = reqBody[guard];
|
|
25
46
|
});
|
|
@@ -34,7 +34,19 @@ function getAppKey(cb) {
|
|
|
34
34
|
|
|
35
35
|
function HashIt(txt, app_key, iteration = 10000, len = 10, cb) {
|
|
36
36
|
const crypIt = new Cryptr(secret.toString().concat(app_key.toString()), { encoding: "base64url", pbkdf2Iterations: iteration, saltLength: len, });
|
|
37
|
-
|
|
37
|
+
let result = "";
|
|
38
|
+
let loop = 0;
|
|
39
|
+
const maxLoop = 10;
|
|
40
|
+
do {
|
|
41
|
+
const payload = txt.concat(md5(secret).toString().slice(0, len + 1));
|
|
42
|
+
result = crypIt.encrypt(payload);
|
|
43
|
+
loop++;
|
|
44
|
+
if (loop >= maxLoop) {
|
|
45
|
+
console.log("\n[101m FAIL [0m Hash loop limit exceeded, Try again.");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
} while (/[+-]/.test(result)); // reject -, +
|
|
49
|
+
cb(result);
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
function DeHashIt(txtHashed, iteration = 10000, len = 10, cb) {
|
|
@@ -43,7 +55,7 @@ function DeHashIt(txtHashed, iteration = 10000, len = 10, cb) {
|
|
|
43
55
|
if(err) {
|
|
44
56
|
cb(err, null);
|
|
45
57
|
} else {
|
|
46
|
-
const crypIt = new Cryptr(secret.toString().concat(app_key.toString()), { encoding: "
|
|
58
|
+
const crypIt = new Cryptr(secret.toString().concat(app_key.toString()), { encoding: "base64", pbkdf2Iterations: iteration, saltLength: len, });
|
|
47
59
|
let decryped = crypIt.decrypt(txtHashed);
|
|
48
60
|
cb(false, decryped.concat(md5(secret).toString()));
|
|
49
61
|
}
|
|
@@ -1,34 +1,60 @@
|
|
|
1
|
-
function findPassportPk(pool_base, pool, passportTable, passportConfigField, cb) {
|
|
1
|
+
async function findPassportPk(pool_base, pool, passportTable, passportConfigField, cb) {
|
|
2
2
|
try {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
let finalPassportField = [];
|
|
4
|
+
// Push and Replace it
|
|
5
|
+
const pushOrReplace = (arr, value) => {
|
|
6
|
+
arr = arr.filter(v => !value.includes(v));
|
|
7
|
+
arr = [...value, ...arr];
|
|
8
|
+
return arr;
|
|
9
|
+
}
|
|
10
|
+
// Check pool engine
|
|
11
|
+
if(pool_base == "basic") {
|
|
12
|
+
// pool base is Basic
|
|
13
|
+
pool.query("SHOW KEYS FROM " + passportTable + " WHERE Key_name = 'PRIMARY'", (err, pk) => {
|
|
14
|
+
if(err) {
|
|
15
|
+
throw "Authentication table: " + err;
|
|
16
|
+
} else {
|
|
17
|
+
if(passportConfigField.length) {
|
|
18
|
+
finalPassportField = pushOrReplace(passportConfigField, [pk[0].Column_name]);
|
|
19
|
+
cb(null, finalPassportField);
|
|
11
20
|
} else {
|
|
12
21
|
cb(null, [pk[0].Column_name]);
|
|
13
22
|
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
} else if (pool_base == "sequelize") {
|
|
26
|
+
// pool base is Sequelize
|
|
27
|
+
// Find table primaryKey
|
|
28
|
+
try {
|
|
29
|
+
const tableInfo = await pool.getQueryInterface().describeTable(String(passportTable));
|
|
30
|
+
const primaryKeys = Object.entries(tableInfo).filter(([columnName, columnInfo]) => columnInfo.primaryKey).map(([columnName]) => columnName);
|
|
31
|
+
if(primaryKeys.length) {
|
|
32
|
+
if(passportConfigField.length) {
|
|
33
|
+
finalPassportField = pushOrReplace(passportConfigField, primaryKeys);
|
|
34
|
+
cb(null, finalPassportField);
|
|
35
|
+
} else {
|
|
36
|
+
cb(null, primaryKeys);
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
if(passportConfigField.length) {
|
|
40
|
+
finalPassportField = pushOrReplace(passportConfigField, [Object.keys(tableInfo)[0]]);
|
|
41
|
+
cb(null, finalPassportField);
|
|
42
|
+
} else {
|
|
43
|
+
cb(null, [Object.keys(tableInfo)[0]]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw `Query Interface ${error}`;
|
|
24
48
|
}
|
|
49
|
+
} else {
|
|
50
|
+
throw "The Base pool error. UNKNOWN pool_base = '"+ pool_base +"'";
|
|
25
51
|
}
|
|
26
52
|
} catch (error) {
|
|
27
53
|
cb(error, null);
|
|
28
54
|
}
|
|
29
55
|
}
|
|
30
56
|
|
|
31
|
-
function checkAuthFields(pool_base, pool, passportTable, passportConfigField, cb) {
|
|
57
|
+
async function checkAuthFields(pool_base, pool, passportTable, passportConfigField, cb) {
|
|
32
58
|
try {
|
|
33
59
|
if(passportConfigField.length) {
|
|
34
60
|
if(pool_base == "basic") {
|
|
@@ -42,18 +68,36 @@ function checkAuthFields(pool_base, pool, passportTable, passportConfigField, cb
|
|
|
42
68
|
});
|
|
43
69
|
} else if (pool_base == "sequelize") {
|
|
44
70
|
// pool base is Sequelize
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
71
|
+
// Check assing fields exists
|
|
72
|
+
const checkColumnsExist = async (tableName, fields = []) => {
|
|
73
|
+
const queryInterface = pool.getQueryInterface();
|
|
74
|
+
try {
|
|
75
|
+
const tableDescription = await queryInterface.describeTable(tableName);
|
|
76
|
+
const result = {};
|
|
77
|
+
for (const column of fields) {
|
|
78
|
+
result[column] = tableDescription.hasOwnProperty(column);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
throw `Query Interface ${error}`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const openFields = await checkColumnsExist(passportTable, passportConfigField);
|
|
86
|
+
const assignFieldsIsWhitelist = Object.values(openFields).includes(false) ? false : true;
|
|
87
|
+
if(assignFieldsIsWhitelist) {
|
|
88
|
+
cb(null, openFields);
|
|
89
|
+
} else {
|
|
90
|
+
cb(`Authentication table fields error: '${passportTable}' => [${passportConfigField}]`, null);
|
|
91
|
+
}
|
|
50
92
|
} else {
|
|
51
93
|
cb("The Base pool error. UNKNOWN pool_base = '"+ pool_base +"'", null);
|
|
52
94
|
}
|
|
95
|
+
} else {
|
|
96
|
+
cb(null, []);
|
|
53
97
|
}
|
|
54
98
|
} catch (error) {
|
|
55
99
|
cb(error, null);
|
|
56
100
|
}
|
|
57
101
|
}
|
|
58
102
|
|
|
59
|
-
module.exports = { findPassportPk, checkAuthFields }
|
|
103
|
+
module.exports = { findPassportPk, checkAuthFields };
|