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,739 +1,1082 @@
1
- #!/usr/bin/env node
2
- const logUpdate = require("log-update");
3
- const inquirer = require('inquirer');
4
- const walk = require("walk");
5
-
6
- class Generator {
7
- constructor() {
8
- this.embed(process.argv)
9
- .then(() => this.init()
10
- .then(status => console.log(status))
11
- .catch(err => {
12
- throw err;
13
- })
14
- )
15
- .catch(err => {
16
- throw err
17
- });
18
- }
19
-
20
- init() {
21
- return new Promise((resolve, reject) => {
22
- try {
23
- if (this.option == '-v' || this.option == '--version') {
24
- // check beech version
25
- resolve("v" + require(__dirname + "/../../../../package.json").version);
26
- } else if (this.option == '-h' || this.option == '?' || this.option == '--help') {
27
- // help for see avaliable command
28
- this.help()
29
- .then(help => resolve(help))
30
- .catch(err => reject(err));
31
- } else if (this.option == 'make') {
32
- // generate endpoint
33
- if (!this.argument) {
34
- resolve("\n Warning  Please specify endpoints name.");
35
- } else {
36
- if (!this.special || this.special == 'undefined') {
37
- this.make()
38
- .then(make => resolve(make))
39
- .catch(err => reject(err));
40
- } else if (this.special == '--require' || this.special == '-R') {
41
- // walking model files
42
- const walkModelPromise = new Promise((resolve) => {
43
- let walker = walk.walk("./src/models", { followLinks: false });
44
- let modelFiles = [];
45
- walker.on("file", (root, stat, next) => {
46
- let subFolderModel = root.split("src/models\\")[1];
47
- modelFiles.push((subFolderModel ? subFolderModel + "/" : "") + stat.name.split('.')[0]);
48
- next();
49
- });
50
- walker.on("end", () => {
51
- if(modelFiles.length) {
52
- inquirer.prompt([ {
53
- type: "checkbox",
54
- name: "selectModel",
55
- message: "Please select Models:",
56
- choices: modelFiles.map(e => e.replace(/\\/g, "/")),
57
- } ]).then(selected => {
58
- resolve(selected.selectModel);
59
- });
60
- } else {
61
- // model file not found, Only create endpoint
62
- resolve();
63
- }
64
- });
65
- });
66
- Promise.all([walkModelPromise]).then((modelSelected) => {
67
- let myModel = modelSelected[0];
68
- // check require model exists
69
- const modelExistsPromise = new Promise((resolve, reject) => {
70
- this.isModelFound(myModel)
71
- .then(existsModel => {
72
- // check exists model
73
- if (existsModel == false) {
74
- inquirer.prompt([ {
75
- type: "confirm",
76
- name: "confirmModelNF",
77
- message: "Model is not found, Do you only create Endpoint ?:",
78
- } ]).then(confirm => {
79
- if(confirm.confirmModelNF) {
80
- resolve([true, []]);
81
- } else {
82
- resolve([false, []]);
83
- }
84
- });
85
- } else {
86
- resolve([true, myModel]);
87
- }
88
- })
89
- .catch(err => reject(err));
90
- });
91
- // promise all check choose model(s)
92
- Promise.all([modelExistsPromise]).then((modelRes) => {
93
- // Say Yes, Making...
94
- if(modelRes[0][0]) {
95
- const poolBasePromise = new Promise((resolve) => {
96
- // Check global config for prepare tmp endpoint
97
- this.fs.readFile("./global.config.js", 'utf8', (err, data) => {
98
- if (err) {
99
- console.log("\n Faltal  Can't read `global.config.js` file.\n", err);
100
- return; // break;
101
- } else {
102
- let buffer = Buffer.from(data);
103
- let buf2str = buffer.toString();
104
- let buf2json = JSON.parse(JSON.stringify(buf2str));
105
- let pool_base = /global.pool_base\s+=\s+(?:"|')([^"]+)(?:"|')(?:\r|\n|$|;|\r)/i.exec(buf2json);
106
- if (pool_base) {
107
- let myRequire = modelRes[0][1];
108
- if (pool_base[ 1 ] == "basic") {
109
- if(myRequire.length) {
110
- // declare basic require model file
111
- let rqr = "";
112
- myRequire.map((data, key) => {
113
- let modelName = data.split('/');
114
- let modelFolder = "";
115
- modelName = modelName.pop();
116
- modelName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
117
- let newModel = modelName.split("_").map(e => e.charAt(0).toUpperCase() + e.slice(1)).join("");
118
- modelFolder = data.substring(0, data.lastIndexOf('/') + 1).replace(/\\/g, "/");
119
- rqr += `const ${newModel} = require(\"@/models/${modelFolder + modelName}\");\n`;
120
- if(myRequire.length == key+1) {
121
- resolve([[rqr], myRequire]);
122
- }
123
- });
124
- } else {
125
- resolve([[]]);
126
- }
127
- } else if (pool_base[ 1 ] == "sequelize") {
128
- // check null require resolve it
129
- if(myRequire.length) {
130
- // declare basic require model file
131
- let rqr = "";
132
- myRequire.map((data, key) => {
133
- let modelName = data.split('/');
134
- let modelFolder = "";
135
- modelName = modelName.pop();
136
- modelName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
137
- let newModel = modelName.split("_").map(e => e.charAt(0).toUpperCase() + e.slice(1)).join("");
138
- modelFolder = data.substring(0, data.lastIndexOf('/') + 1).replace(/\\/g, "/");
139
- rqr += `const { ${newModel} } = require(\"@/models/${modelFolder + modelName}\");\n`;
140
- if(myRequire.length == key+1) {
141
- resolve([[rqr], myRequire]);
142
- }
143
- });
144
- } else {
145
- resolve([[]]);
146
- }
147
- } else {
148
- console.log("\n Faltal  The pool_base in `global.config.js` file does not match the specific.");
149
- }
150
- } else {
151
- console.log("\n Faltal  The pool_base in `global.config.js` file is not found.");
152
- }
153
- }
154
- });
155
- });
156
- // Final promise for make
157
- Promise.all([poolBasePromise]).then((rqrRes) => {
158
- // make with require model file
159
- this.make(rqrRes[0])
160
- .then(make => resolve(make))
161
- .catch(err => reject(err));
162
- });
163
- } else {
164
- // Say No, Nothing...
165
- resolve(": Say no.");
166
- }
167
- });
168
- });
169
- } else if (this.special == '--model' || this.special == '-M') {
170
- this.makeModel()
171
- .then(make => resolve(make))
172
- .catch(err => reject(err));
173
- } else if (this.special == '--helper') {
174
- this.makeHelper()
175
- .then(make => resolve(make))
176
- .catch(err => reject(err));
177
- } else {
178
- resolve("\n Faltal  commnad it's not available.");
179
- }
180
- }
181
- } else if (this.option == 'passport') {
182
- if (this.argument == "init") {
183
- this.makePassportInit()
184
- .then(make => resolve(make))
185
- .catch(err => reject(err));
186
- } else {
187
- resolve("\n Warning  Using `passport init` for initiate passport-jwt.");
188
- }
189
- } else if (this.option == "key:generate" || this.option == "key:gen") {
190
- this.generateKeyConfigFile()
191
- .then(resGenKey => resolve(resGenKey))
192
- .catch(err => reject(err));
193
- } else if (this.option && this.option.slice(0, 5) == 'hash:') {
194
- if(this.option.length > 5) {
195
- const { HashIt, Z } = require(__dirname + "/../helpers/math");
196
- Z((err, ak) => {
197
- if(err) {
198
- logUpdate(err);
199
- } else {
200
- let txt = this.option.split(":");
201
- HashIt(txt, ak, null, (5).toString().length, (hashed) => {
202
- logUpdate(hashed);
203
- });
204
- }
205
- });
206
- } else {
207
- resolve("\n Info.  No text to hash.");
208
- }
209
- } else if (this.option == "skd") {
210
- if (this.argument == "init") {
211
- this.makeAddOnInit()
212
- .then(make => resolve(make))
213
- .catch(err => reject(err));
214
- } else {
215
- resolve("\n Warning  Using `skd init` for initiate Job Scheduler.");
216
- }
217
- } else {
218
- // help for see avaliable command
219
- this.help()
220
- .then(help => resolve(help))
221
- .catch(err => reject(err));
222
- }
223
- } catch (error) {
224
- reject(error);
225
- }
226
- });
227
- }
228
-
229
- make(rq = null) {
230
- return new Promise((resolve, reject) => {
231
- try {
232
- // prepare data
233
- let tmpEndpointsPath = __dirname;
234
- let tmpSpecPath = __dirname + '/_spec';
235
- let endpointsPath = './src/endpoints/';
236
- let testPath = './__tests__/unit/endpoints/';
237
- // argument join `slash`
238
- let arg = this.argument.replace(/^\/+|\/+$/g, '');
239
- arg = arg.split('/');
240
- let endpoints = arg.pop();
241
- let subFolder = arg.join('/');
242
- // endpoints file
243
- let fullEndpoints = endpointsPath + subFolder.concat('/') + endpoints.concat('-endpoints.js');
244
- let routeEndpoints = ((arg.length > 0) ? '/' : '') + subFolder.concat('/') + endpoints;
245
- // test file
246
- let fullTest = testPath + subFolder + '/' + endpoints.concat('-endpoints.spec.js');
247
-
248
- // Check exists endpoint file
249
- if (!this.fs.existsSync(fullEndpoints)) {
250
- // STEP 0 : Check global config for prepare tmp endpoint
251
- const promise0 = new Promise((resolve) => {
252
- this.fs.readFile("./global.config.js", 'utf8', (err, data) => {
253
- if (err) {
254
- console.log("\n Faltal  Can't read `global.config.js` file.", err);
255
- resolve([false, null, null]);
256
- } else {
257
- let buffer = Buffer.from(data);
258
- let buf2str = buffer.toString();
259
- let buf2json = JSON.parse(JSON.stringify(buf2str));
260
- let pool_base = /global.pool_base\s+=\s+(?:"|')([^"]+)(?:"|')(?:\r|\n|$|;|\r)/i.exec(buf2json);
261
- if (pool_base) {
262
- if (pool_base[ 1 ] == "basic") {
263
- resolve([true, tmpEndpointsPath += '/_endpoints_basic', pool_base[ 1 ]]);
264
- } else if (pool_base[ 1 ] == "sequelize") {
265
- resolve([true, tmpEndpointsPath += '/_endpoints', pool_base[ 1 ]]);
266
- } else {
267
- console.log("\n Faltal  The pool_base in `global.config.js` file does not match the specific.");
268
- resolve([false, null, null]);
269
- }
270
- } else {
271
- console.log("\n Faltal  The pool_base in `global.config.js` file is not found.");
272
- resolve([false, null, null]);
273
- }
274
- }
275
- });
276
- });
277
-
278
- // STEP 1 : Format Require model file
279
- const promise1 = new Promise((resolve) => {
280
- // prepare state require file if `rq[0]` not exists
281
- let requireFile = '// You can require something \n';
282
- // check exists requrie files
283
- if(rq) {
284
- if (rq[0].length) {
285
- requireFile = '';
286
- // make require multiples line
287
- rq[0].map((data, key) => {
288
- requireFile += data;
289
- if(rq[0].length == key+1) {
290
- resolve(requireFile);
291
- }
292
- })
293
- } else {
294
- resolve(requireFile);
295
- }
296
- } else {
297
- resolve(requireFile);
298
- }
299
- });
300
- // promise all generate endpoint with require(s)
301
- Promise.all([promise0, promise1]).then((rqFileRes) => {
302
- /**
303
- * @return
304
- *
305
- * rqFileRes[0] : Array[0 = global file true, 1 = tmp endpoint file, 2 = pool_base type ]
306
- * rqFileRes[1] : Text require file
307
- *
308
- */
309
- // check global file exists.
310
- if(rqFileRes[0][0]) {
311
- logUpdate(": Initialize...");
312
- // check for remove first slash/backslash and replace / or \ or multiple slash/backslash to one slash for route endpoint name
313
- routeEndpoints = routeEndpoints.replace(/^[\\\/]+/, "").replace(/[\\\/]+/g, "/");
314
- // timeout generate endpoint and replace content
315
- setTimeout(() => {
316
- // generater endpoint
317
- this.makeFolder(endpointsPath + subFolder)
318
- .then(this.copy.bind(this, tmpEndpointsPath, fullEndpoints))
319
- .then(this.contentReplace.bind(this, fullEndpoints, {
320
- 'endpoint': routeEndpoints,
321
- 'endpointName': endpoints,
322
- 'rq': rqFileRes[1],
323
- }))
324
- // generater test
325
- .then(this.makeFolder.bind(this, testPath + subFolder))
326
- .then(this.copy.bind(this, tmpSpecPath, fullTest))
327
- .then(this.contentReplace.bind(this, fullTest, {
328
- 'endpoint': routeEndpoints,
329
- 'endpointName': endpoints
330
- }))
331
- .then(logUpdate("\n Processing   The endpoint `" + endpoints + "` it's generating..."))
332
- .then(generated => logUpdate(generated))
333
- .catch(err => {
334
- throw err;
335
- });
336
- }, 2000);
337
- }
338
- });
339
- } else {
340
- resolve("\n Warning  The endpoint `" + endpoints + "` it's duplicated.");
341
- }
342
- } catch (error) {
343
- reject(error);
344
- }
345
- });
346
- }
347
-
348
- isModelFound(modelArr) {
349
- return new Promise((resolve, reject) => {
350
- try {
351
- if(modelArr !== undefined) {
352
- if(modelArr.length) {
353
- modelArr.map((data, key) => {
354
- if (!this.fs.existsSync('./src/models/' + data.concat('.js'))) {
355
- resolve(data);
356
- }
357
- if (modelArr.length == key+1) {
358
- resolve(true);
359
- }
360
- });
361
- } else {
362
- resolve(false);
363
- }
364
- } else {
365
- resolve(false);
366
- }
367
- } catch (error) {
368
- reject(error);
369
- }
370
- });
371
- }
372
-
373
- makeModel() {
374
- return new Promise((resolve, reject) => {
375
- try {
376
- // declare path model file
377
- let tmpModelsPath = __dirname;
378
- // read global.config.js file for check pool_base for generate model file
379
- this.fs.readFile("./global.config.js", 'utf8', (err, data) => {
380
- if (err) {
381
- resolve("\n Faltal  Can't read `global.config.js` file.", err);
382
- } else {
383
- let buffer = Buffer.from(data);
384
- let buf2str = buffer.toString();
385
- let buf2json = JSON.parse(JSON.stringify(buf2str));
386
- let pool_base = /global.pool_base\s+=\s+(?:"|')([^"]+)(?:"|')(?:\r|\n|$|;|\r)/i.exec(buf2json);
387
- if (pool_base) {
388
- // read app.config.js file for get db connect name
389
- this.fs.readFile("./app.config.js", 'utf8', (appErr, appData) => {
390
- if (appErr) {
391
- resolve("\n Faltal  Can't read `app.config.js` file.", appErr);
392
- } else {
393
- let appBuffer = Buffer.from(appData);
394
- let appBuf2str = appBuffer.toString();
395
- let appBuf2json = JSON.parse(JSON.stringify(appBuf2str));
396
- let appBuf2eval = eval(appBuf2json);
397
- // choose one of database connect name
398
- inquirer.prompt([ {
399
- type: "list",
400
- name: "selectDbConnect",
401
- message: "Please select database connect name:",
402
- choices: appBuf2eval.database_config.map(e => e.name),
403
- } ]).then(dbSelected => {
404
- // check pool_base
405
- if (pool_base[ 1 ] == "basic") {
406
- tmpModelsPath += '/_models_basic';
407
- this.generateModel(tmpModelsPath, dbSelected.selectDbConnect)
408
- .then(console.log)
409
- .catch(console.log);
410
- } else if (pool_base[ 1 ] == "sequelize") {
411
- tmpModelsPath += '/_models';
412
- this.generateModel(tmpModelsPath, dbSelected.selectDbConnect)
413
- .then(console.log)
414
- .catch(console.log);
415
- } else {
416
- resolve("\n Faltal  The pool_base in `global.config.js` file does not match the specific.");
417
- }
418
- });
419
- }
420
- });
421
- } else {
422
- resolve("\n Faltal  The pool_base in `global.config.js` file is not found.");
423
- }
424
- }
425
- });
426
- } catch (error) {
427
- reject(error);
428
- }
429
- });
430
- }
431
-
432
- generateModel(tmpModelsPath, dbSelected) {
433
- return new Promise((resolve, reject) => {
434
- try {
435
- let modelPath = './src/models/';
436
- // argument join `slash`
437
- let arg = this.argument.replace(/^\/+|\/+$/g, '');
438
- arg = arg.split('/');
439
- let models = arg.pop();
440
- models = models.charAt(0).toUpperCase() + models.slice(1);
441
- let newModel = models.split("_").map(e => e.charAt(0).toUpperCase() + e.slice(1)).join("");
442
- let subFolder = arg.join('/');
443
- // models
444
- let fullModels = modelPath + subFolder.concat('/') + models.concat('.js');
445
-
446
- // check file exists
447
- if (!this.fs.existsSync(fullModels)) {
448
- // generater model
449
- this.makeFolder(modelPath + subFolder)
450
- .then(this.copy.bind(this, tmpModelsPath, fullModels))
451
- .then(this.modelContentReplace.bind(this, fullModels, {
452
- 'modelNameUppercase': newModel,
453
- 'modelName': models.toLowerCase(),
454
- 'dbSelected': dbSelected,
455
- }))
456
- .then(logUpdate("\n Processing  The model `" + models + "` it's generating..."))
457
- .then(generated => logUpdate(generated))
458
- .catch(err => {
459
- throw err;
460
- });
461
- } else {
462
- resolve("\n Warning  The model `" + models + "` it's duplicated.");
463
- }
464
- } catch (error) {
465
- reject(error);
466
- }
467
- });
468
- }
469
-
470
- makeHelper() {
471
- return new Promise((resolve, reject) => {
472
- try {
473
- let tmpHelpersPath = __dirname + '/_helpers';
474
- let helperPath = './src/helpers/';
475
- // argument join `slash`
476
- let arg = this.argument.replace(/^\/+|\/+$/g, '');
477
- arg = arg.split('/');
478
- let helpers = arg.pop();
479
- helpers = helpers.charAt(0).toUpperCase() + helpers.slice(1);
480
- let subFolder = arg.join('/');
481
- // helpers
482
- let fullHelpers = helperPath + subFolder.concat('/') + helpers.concat('.js');
483
- // check file exists
484
- if (!this.fs.existsSync(fullHelpers)) {
485
- // generater model
486
- this.makeFolder(helperPath + subFolder)
487
- .then(this.copy.bind(this, tmpHelpersPath, fullHelpers))
488
- .then(logUpdate("\n Processing  The helper `" + helpers + "` it's generating..."))
489
- .then(logUpdate("\n Passed  The helper `" + helpers + "` it's generated."))
490
- .catch(err => {
491
- throw err;
492
- });
493
- } else {
494
- resolve("\n Warning  The helper `" + helpers + "` it's duplicated.");
495
- }
496
- } catch (error) {
497
- reject(error);
498
- }
499
- });
500
- }
501
-
502
- makeFolder(path) {
503
- /**
504
- * @param path path to make
505
- *
506
- * @return new full path
507
- *
508
- */
509
- return new Promise((resolve, reject) => {
510
- try {
511
- const mkdirp = require("mkdirp");
512
- mkdirp(path)
513
- .then(p => resolve(p))
514
- .catch(err => reject(err));
515
- } catch (error) {
516
- reject(error);
517
- }
518
- });
519
- }
520
-
521
- makePassportInit() {
522
- return new Promise((resolve, reject) => {
523
- try {
524
- let passport_config_file = __dirname + '/../configure/passport.config.js';
525
- let passport_config_paste_point = "./passport.config.js";
526
- if (!this.fs.existsSync(passport_config_paste_point)) {
527
- this.copy(passport_config_file, passport_config_paste_point)
528
- .then(resolve("\n Passed  The `passport-jwt` is initialized."))
529
- .catch(err => console.log(err));
530
- } else {
531
- resolve("\n Warning  The `passport-jwt` already is initialized.");
532
- }
533
- } catch (error) {
534
- reject(error);
535
- }
536
- });
537
- }
538
-
539
- makeAddOnInit() {
540
- return new Promise((resolve, reject) => {
541
- try {
542
- let tmpEndpointsPath = __dirname + '/_scheduler';
543
- let add_on_paste_point = "Scheduler.js";
544
- let folder_add_on = "./src/";
545
- if (!this.fs.existsSync(folder_add_on + add_on_paste_point)) {
546
- this.makeFolder(folder_add_on)
547
- .then(this.copy.bind(this, tmpEndpointsPath, folder_add_on + add_on_paste_point))
548
- .then(resolve("\n Passed  The `Scheduler` is initialized."))
549
- .catch(err => console.log(err));
550
- } else {
551
- resolve("\n Warning  The `Scheduler` already is initialized.");
552
- }
553
- } catch (error) {
554
- reject(error);
555
- }
556
- });
557
- }
558
-
559
- copy(path, to) {
560
- /**
561
- * @param path old path file
562
- * @param to save to new path file
563
- *
564
- * @return new path file
565
- */
566
- return new Promise((resolve, reject) => {
567
- try {
568
- if (!this.fs.existsSync(to)) {
569
- if (this.fs.ReadStream(path).pipe(this.fs.createWriteStream(to))) {
570
- resolve(to);
571
- } else {
572
- throw err;
573
- }
574
- } else {
575
- resolve(to);
576
- }
577
- } catch (error) {
578
- reject(error);
579
- }
580
- });
581
- }
582
-
583
- contentReplace(pathFile, textCondition) {
584
- return new Promise((resolve, reject) => {
585
- try {
586
- let endpoint = textCondition.endpoint;
587
- let endpointName = textCondition.endpointName;
588
- let rq = textCondition.rq;
589
- let modelName = textCondition.modelName;
590
- // delay for generator
591
- setTimeout(() => {
592
- this.fs.readFile(pathFile, 'utf8', (err, data) => {
593
- if (err) {
594
- throw err;
595
- } else {
596
- // content replace
597
- let text = data.replace(new RegExp('{{endpoint}}', 'g'), endpoint);
598
- text = text.replace(new RegExp('{{endpointName}}', 'g'), endpointName);
599
- text = text.replace(new RegExp('{{requireSomething}}', 'g'), rq);
600
- text = text.replace(new RegExp('{{modelName}}', 'g'), modelName);
601
- setTimeout(() => {
602
- // writing the file
603
- this.fs.writeFile(pathFile, text, 'utf8', (err) => {
604
- if (err) {
605
- throw err;
606
- } else {
607
- resolve("\n Passed  The endpoint `" + endpointName + "` it's generated.");
608
- }
609
- });
610
- }, 1000);
611
- }
612
- })
613
- }, 1000);
614
- } catch (error) {
615
- reject(error);
616
- }
617
- });
618
- }
619
-
620
- modelContentReplace(pathFile, textCondition) {
621
- return new Promise((resolve, reject) => {
622
- try {
623
- let modelName = textCondition.modelName;
624
- // delay for generator
625
- setTimeout(() => {
626
- this.fs.readFile(pathFile, 'utf8', (err, data) => {
627
- if (err) {
628
- throw err;
629
- } else {
630
- // content replace
631
- let text = data.replace(new RegExp('{{modelName}}', 'g'), modelName);
632
- text = text.replace(new RegExp('{{dbSelected}}', 'g'), textCondition.dbSelected);
633
- // check add model name text uppercase
634
- if (Object.keys(textCondition).length > 1) {
635
- text = text.replace(new RegExp('{{modelNameUppercase}}', 'g'), textCondition.modelNameUppercase);
636
- }
637
- // writing the file
638
- this.fs.writeFile(pathFile, text, 'utf8', (err) => {
639
- if (err) {
640
- throw err;
641
- } else {
642
- resolve("\n Passed  The model `" + modelName + "` it's generated.");
643
- }
644
- });
645
- }
646
- })
647
- }, 1000);
648
- } catch (error) {
649
- reject(error);
650
- }
651
- });
652
- }
653
-
654
- appKeyGenerator(length) {
655
- return new Promise((resolve, reject) => {
656
- try {
657
- let md5 = require("md5");
658
- let secret = require(__dirname + "/../../../lib/src/salt").salt;
659
- let result = '';
660
- let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
661
- let charactersLength = characters.length;
662
- for (var i = 0; i < length; i++) {
663
- result += characters.charAt(Math.floor(Math.random() * charactersLength));
664
- }
665
- resolve(md5(result + secret));
666
- } catch (error) {
667
- reject(error);
668
- }
669
- });
670
- }
671
-
672
- generateKeyConfigFile() {
673
- return new Promise((resolve, reject) => {
674
- try {
675
- this.fs.readFile("app.config.js", 'utf8', (err, data) => {
676
- if (err) {
677
- throw err;
678
- } else {
679
- // edit or add property
680
- let buffer = Buffer.from(data);
681
- let buf2str = buffer.toString();
682
- let buf2json = JSON.parse(JSON.stringify(buf2str));
683
- let buf2eval = eval(buf2json);
684
- let oldSecret = buf2eval.main_config.app_key;
685
- // generate new key secret
686
- this.appKeyGenerator(8).then(newAppSecret => {
687
- // content replace
688
- let text = data.replace(new RegExp(oldSecret, 'g'), newAppSecret);
689
- // writing the file
690
- this.fs.writeFile("app.config.js", text, 'utf8', (err) => {
691
- if (err) {
692
- throw err;
693
- } else {
694
- resolve("\n Passed  App secret it's new generated.");
695
- }
696
- });
697
- });
698
- }
699
- });
700
- } catch (error) {
701
- reject(error);
702
- }
703
- });
704
- }
705
-
706
- help() {
707
- return new Promise((resolve, reject) => {
708
- try {
709
- this.fs.readFile(__dirname + "/_help", 'utf8', function (err, data) {
710
- if (err) {
711
- throw err;
712
- }
713
- resolve(data);
714
- });
715
- } catch (error) {
716
- reject(err);
717
- }
718
- });
719
- }
720
-
721
- embed(argv) {
722
- return new Promise((resolve, reject) => {
723
- try {
724
- this.fs = require('fs');
725
- this.cmd = require('node-cmd');
726
- this.argv = argv;
727
- this.option = argv[ 2 ];
728
- this.argument = argv[ 3 ];
729
- this.special = argv[ 4 ];
730
- this.extra = argv[ 5 ];
731
- resolve(this);
732
- } catch (error) {
733
- reject(err);
734
- }
735
- });
736
- }
737
- }
738
-
1
+ #!/usr/bin/env node
2
+ const logUpdate = require("log-update");
3
+ const inquirer = require('inquirer');
4
+ const walk = require("walk");
5
+ const recast = require("recast");
6
+ const { builders } = require("ast-types");
7
+ const { connectForGenerateModel } = require("../databases/test");
8
+
9
+ class Generator {
10
+ constructor() {
11
+ this.embed(process.argv)
12
+ .then(() => this.init()
13
+ .then(status => console.log(status))
14
+ .catch(err => {
15
+ throw err;
16
+ })
17
+ )
18
+ .catch(err => {
19
+ throw err
20
+ });
21
+ }
22
+
23
+ init() {
24
+ return new Promise((resolve, reject) => {
25
+ try {
26
+ if (this.option == '-v' || this.option == '--version') {
27
+ // check beech version
28
+ resolve("v" + require(__dirname + "/../../../../package.json").version);
29
+ } else if (this.option == '-h' || this.option == '?' || this.option == '--help') {
30
+ // help for see avaliable command
31
+ this.help()
32
+ .then(help => resolve(help))
33
+ .catch(err => reject(err));
34
+ } else if (this.option == 'make') {
35
+ // generate endpoint
36
+ if (!this.argument) {
37
+ resolve("\n Warning  Please specify endpoints name.");
38
+ } else {
39
+ if (!this.special || this.special == 'undefined') {
40
+ this.make()
41
+ .then(make => resolve(make))
42
+ .catch(err => reject(err));
43
+ } else if (this.special == '--require' || this.special == '-R') {
44
+ // walking model files
45
+ const walkModelPromise = new Promise((resolve) => {
46
+ let walker = walk.walk("./src/models", { followLinks: false });
47
+ let modelFiles = [];
48
+ walker.on("file", (root, stat, next) => {
49
+ let subFolderModel = root.split("src/models\\")[1];
50
+ modelFiles.push((subFolderModel ? subFolderModel + "/" : "") + stat.name.split('.')[0]);
51
+ next();
52
+ });
53
+ walker.on("end", () => {
54
+ if(modelFiles.length) {
55
+ inquirer.prompt([ {
56
+ type: "checkbox",
57
+ name: "selectModel",
58
+ message: "Please select Models:",
59
+ choices: modelFiles.map(e => e.replace(/\\/g, "/")),
60
+ } ]).then(selected => {
61
+ resolve(selected.selectModel);
62
+ });
63
+ } else {
64
+ // model file not found, Only create endpoint
65
+ resolve();
66
+ }
67
+ });
68
+ });
69
+ Promise.all([walkModelPromise]).then((modelSelected) => {
70
+ let myModel = modelSelected[0];
71
+ // check require model exists
72
+ const modelExistsPromise = new Promise((resolve, reject) => {
73
+ this.isModelFound(myModel)
74
+ .then(existsModel => {
75
+ // check exists model
76
+ if (existsModel == false) {
77
+ inquirer.prompt([ {
78
+ type: "confirm",
79
+ name: "confirmModelNF",
80
+ message: "Model is not found, Do you only create Endpoint ?:",
81
+ } ]).then(confirm => {
82
+ if(confirm.confirmModelNF) {
83
+ resolve([true, []]);
84
+ } else {
85
+ resolve([false, []]);
86
+ }
87
+ });
88
+ } else {
89
+ resolve([true, myModel]);
90
+ }
91
+ })
92
+ .catch(err => reject(err));
93
+ });
94
+ // promise all check choose model(s)
95
+ Promise.all([modelExistsPromise]).then((modelRes) => {
96
+ // Say Yes, Making...
97
+ if(modelRes[0][0]) {
98
+ const poolBasePromise = new Promise((resolve) => {
99
+ // Check global config for prepare tmp endpoint
100
+ this.fs.readFile("./global.config.js", 'utf8', (err, data) => {
101
+ if (err) {
102
+ console.log("\n Fatal  Can't read `global.config.js` file.\n", err);
103
+ return; // break;
104
+ } else {
105
+ let buffer = Buffer.from(data);
106
+ let buf2str = buffer.toString();
107
+ let buf2json = JSON.parse(JSON.stringify(buf2str));
108
+ let pool_base = /global.pool_base\s+=\s+(?:"|')([^"]+)(?:"|')(?:\r|\n|$|;|\r)/i.exec(buf2json);
109
+ if (pool_base) {
110
+ let myRequire = modelRes[0][1];
111
+ if (pool_base[ 1 ] == "basic") {
112
+ if(myRequire.length) {
113
+ // declare basic require model file
114
+ let rqr = "";
115
+ myRequire.map((data, key) => {
116
+ let modelName = data.split('/');
117
+ let modelFolder = "";
118
+ modelName = modelName.pop();
119
+ modelName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
120
+ let newModel = modelName.split("_").map(e => e.charAt(0).toUpperCase() + e.slice(1)).join("");
121
+ modelFolder = data.substring(0, data.lastIndexOf('/') + 1).replace(/\\/g, "/");
122
+ rqr += `const ${newModel} = require(\"@/models/${modelFolder + modelName}\");\n`;
123
+ if(myRequire.length == key+1) {
124
+ resolve([[rqr], myRequire]);
125
+ }
126
+ });
127
+ } else {
128
+ resolve([[]]);
129
+ }
130
+ } else if (pool_base[ 1 ] == "sequelize") {
131
+ // check null require resolve it
132
+ if(myRequire.length) {
133
+ // declare basic require model file
134
+ let rqr = "";
135
+ myRequire.map((data, key) => {
136
+ let modelName = data.split('/');
137
+ let modelFolder = "";
138
+ modelName = modelName.pop();
139
+ modelName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
140
+ let newModel = modelName.split("_").map(e => e.charAt(0).toUpperCase() + e.slice(1)).join("");
141
+ modelFolder = data.substring(0, data.lastIndexOf('/') + 1).replace(/\\/g, "/");
142
+ rqr += `const { ${newModel} } = require(\"@/models/${modelFolder + modelName}\");\n`;
143
+ if(myRequire.length == key+1) {
144
+ resolve([[rqr], myRequire]);
145
+ }
146
+ });
147
+ } else {
148
+ resolve([[]]);
149
+ }
150
+ } else {
151
+ console.log("\n Fatal  The pool_base in `global.config.js` file does not match the specific.");
152
+ }
153
+ } else {
154
+ console.log("\n Fatal  The pool_base in `global.config.js` file is not found.");
155
+ }
156
+ }
157
+ });
158
+ });
159
+ // Final promise for make
160
+ Promise.all([poolBasePromise]).then((rqrRes) => {
161
+ // make with require model file
162
+ this.make(rqrRes[0])
163
+ .then(make => resolve(make))
164
+ .catch(err => reject(err));
165
+ });
166
+ } else {
167
+ // Say No, Nothing...
168
+ resolve(": Say no.");
169
+ }
170
+ });
171
+ });
172
+ } else if (this.special == '--model' || this.special == '-M') {
173
+ this.makeModel()
174
+ .then(make => resolve(make))
175
+ .catch(err => reject(err));
176
+ } else if (this.special == '--helper') {
177
+ this.makeHelper()
178
+ .then(make => resolve(make))
179
+ .catch(err => reject(err));
180
+ } else {
181
+ resolve("\n Fatal  commnad it's not available.");
182
+ }
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
+ let extra = this.extra || undefined;
191
+ this.updateModel(extra)
192
+ .then(res => resolve(res))
193
+ .catch(err => reject(err));
194
+ }
195
+ } else {
196
+ resolve("\n Warning  Please specify model name to update.");
197
+ }
198
+ } else if(this.argument != 'model') {
199
+ resolve("\n Fatal  commnad it's not available.");
200
+ } else {
201
+ resolve("\n Warning  Please specify what you want to update.");
202
+ }
203
+ } else if (this.option == 'passport') {
204
+ if (this.argument == "init") {
205
+ this.makePassportInit()
206
+ .then(make => resolve(make))
207
+ .catch(err => reject(err));
208
+ } else {
209
+ resolve("\n Warning  Using `passport init` for initiate passport-jwt.");
210
+ }
211
+ } else if (this.option == "key:generate" || this.option == "key:gen") {
212
+ this.generateKeyConfigFile()
213
+ .then(resGenKey => resolve(resGenKey))
214
+ .catch(err => reject(err));
215
+ } else if (this.option && this.option.slice(0, 5) == "hash:") {
216
+ const { HashIt, Z } = require(__dirname + "/../helpers/math");
217
+ if(this.option.length > 5) {
218
+ Z((err, ak) => {
219
+ if(err) {
220
+ logUpdate(err);
221
+ } else {
222
+ let txt = this.option.split(":");
223
+ HashIt(txt, ak, null, (5).toString().length, (hashed) => {
224
+ logUpdate("\n" + hashed);
225
+ });
226
+ }
227
+ });
228
+ } else {
229
+ inquirer.prompt([ {
230
+ type: "confirm",
231
+ name: "confirmHashNoText",
232
+ message: "No text to hash, Do you want to continue ?:",
233
+ } ]).then(confirm => {
234
+ if(confirm.confirmHashNoText) {
235
+ Z((err, ak) => {
236
+ if(err) {
237
+ logUpdate(err);
238
+ } else {
239
+ let txt = this.option.split(":");
240
+ HashIt(txt, ak, null, (5).toString().length, (hashed) => {
241
+ logUpdate("\n" + hashed);
242
+ });
243
+ }
244
+ });
245
+ } else {
246
+ // no text to hash say no.
247
+ resolve(": Say no.");
248
+ }
249
+ });
250
+
251
+ }
252
+ } else if (this.option == "skd") {
253
+ if (this.argument == "init") {
254
+ this.makeAddOnInit()
255
+ .then(make => resolve(make))
256
+ .catch(err => reject(err));
257
+ } else {
258
+ resolve("\n Warning  Using `skd init` for initiate Job Scheduler.");
259
+ }
260
+ } else {
261
+ // help for see avaliable command
262
+ this.help()
263
+ .then(help => resolve(help))
264
+ .catch(err => reject(err));
265
+ }
266
+ } catch (error) {
267
+ reject(error);
268
+ }
269
+ });
270
+ }
271
+
272
+ make(rq = null) {
273
+ return new Promise((resolve, reject) => {
274
+ try {
275
+ // prepare data
276
+ let tmpEndpointsPath = __dirname;
277
+ let tmpSpecPath = __dirname + '/_spec';
278
+ let endpointsPath = './src/endpoints/';
279
+ let testPath = './__tests__/unit/endpoints/';
280
+ // argument join `slash`
281
+ let arg = this.argument.replace(/^\/+|\/+$/g, '');
282
+ arg = arg.split('/');
283
+ let endpoints = arg.pop();
284
+ let subFolder = arg.join('/');
285
+ // endpoints file
286
+ let fullEndpoints = endpointsPath + subFolder.concat('/') + endpoints.concat('-endpoints.js');
287
+ let routeEndpoints = ((arg.length > 0) ? '/' : '') + subFolder.concat('/') + endpoints;
288
+ // test file
289
+ let fullTest = testPath + subFolder + '/' + endpoints.concat('-endpoints.spec.js');
290
+
291
+ // Check exists endpoint file
292
+ if (!this.fs.existsSync(fullEndpoints)) {
293
+ // STEP 0 : Check global config for prepare tmp endpoint
294
+ const promise0 = new Promise((resolve) => {
295
+ this.fs.readFile("./global.config.js", 'utf8', (err, data) => {
296
+ if (err) {
297
+ console.log("\n Fatal  Can't read `global.config.js` file.", err);
298
+ resolve([false, null, null]);
299
+ } else {
300
+ let buffer = Buffer.from(data);
301
+ let buf2str = buffer.toString();
302
+ let buf2json = JSON.parse(JSON.stringify(buf2str));
303
+ let pool_base = /global.pool_base\s+=\s+(?:"|')([^"]+)(?:"|')(?:\r|\n|$|;|\r)/i.exec(buf2json);
304
+ if (pool_base) {
305
+ if (pool_base[ 1 ] == "basic") {
306
+ resolve([true, tmpEndpointsPath += '/_endpoints_basic', pool_base[ 1 ]]);
307
+ } else if (pool_base[ 1 ] == "sequelize") {
308
+ resolve([true, tmpEndpointsPath += '/_endpoints', pool_base[ 1 ]]);
309
+ } else {
310
+ console.log("\n Fatal  The pool_base in `global.config.js` file does not match the specific.");
311
+ resolve([false, null, null]);
312
+ }
313
+ } else {
314
+ console.log("\n Fatal  The pool_base in `global.config.js` file is not found.");
315
+ resolve([false, null, null]);
316
+ }
317
+ }
318
+ });
319
+ });
320
+
321
+ // STEP 1 : Format Require model file
322
+ const promise1 = new Promise((resolve) => {
323
+ // prepare state require file if `rq[0]` not exists
324
+ let requireFile = '// You can require something \n';
325
+ // check exists requrie files
326
+ if(rq) {
327
+ if (rq[0].length) {
328
+ requireFile = '';
329
+ // make require multiples line
330
+ rq[0].map((data, key) => {
331
+ requireFile += data;
332
+ if(rq[0].length == key+1) {
333
+ resolve(requireFile);
334
+ }
335
+ })
336
+ } else {
337
+ resolve(requireFile);
338
+ }
339
+ } else {
340
+ resolve(requireFile);
341
+ }
342
+ });
343
+ // promise all generate endpoint with require(s)
344
+ Promise.all([promise0, promise1]).then((rqFileRes) => {
345
+ /**
346
+ * @return
347
+ *
348
+ * rqFileRes[0] : Array[0 = global file true, 1 = tmp endpoint file, 2 = pool_base type ]
349
+ * rqFileRes[1] : Text require file
350
+ *
351
+ */
352
+ // check global file exists.
353
+ if(rqFileRes[0][0]) {
354
+ logUpdate(": Initialize...");
355
+ // check for remove first slash/backslash and replace / or \ or multiple slash/backslash to one slash for route endpoint name
356
+ routeEndpoints = routeEndpoints.replace(/^[\\\/]+/, "").replace(/[\\\/]+/g, "/");
357
+ // timeout generate endpoint and replace content
358
+ setTimeout(() => {
359
+ // generater endpoint
360
+ this.makeFolder(endpointsPath + subFolder)
361
+ .then(this.copy.bind(this, tmpEndpointsPath, fullEndpoints))
362
+ .then(this.contentReplace.bind(this, fullEndpoints, {
363
+ 'endpoint': routeEndpoints,
364
+ 'endpointName': endpoints,
365
+ 'rq': rqFileRes[1],
366
+ }))
367
+ // generater test
368
+ .then(this.makeFolder.bind(this, testPath + subFolder))
369
+ .then(this.copy.bind(this, tmpSpecPath, fullTest))
370
+ .then(this.contentReplace.bind(this, fullTest, {
371
+ 'endpoint': routeEndpoints,
372
+ 'endpointName': endpoints
373
+ }))
374
+ .then(logUpdate("\n Processing   The endpoint `" + endpoints + "` it's generating..."))
375
+ .then(generated => logUpdate(generated))
376
+ .catch(err => {
377
+ throw err;
378
+ });
379
+ }, 2000);
380
+ }
381
+ });
382
+ } else {
383
+ resolve("\n Warning  The endpoint `" + endpoints + "` it's duplicated.");
384
+ }
385
+ } catch (error) {
386
+ reject(error);
387
+ }
388
+ });
389
+ }
390
+
391
+ isModelFound(modelArr) {
392
+ return new Promise((resolve, reject) => {
393
+ try {
394
+ if(modelArr !== undefined) {
395
+ if(modelArr.length) {
396
+ modelArr.map((data, key) => {
397
+ if (!this.fs.existsSync('./src/models/' + data.concat('.js'))) {
398
+ resolve(data);
399
+ }
400
+ if (modelArr.length == key+1) {
401
+ resolve(true);
402
+ }
403
+ });
404
+ } else {
405
+ resolve(false);
406
+ }
407
+ } else {
408
+ resolve(false);
409
+ }
410
+ } catch (error) {
411
+ reject(error);
412
+ }
413
+ });
414
+ }
415
+
416
+ makeModel() {
417
+ return new Promise((resolve, reject) => {
418
+ try {
419
+ // declare path model file
420
+ let tmpModelsPath = __dirname;
421
+ // read global.config.js file for check pool_base for generate model file
422
+ this.fs.readFile("./global.config.js", 'utf8', (err, data) => {
423
+ if (err) {
424
+ resolve("\n Fatal  Can't read `global.config.js` file.", err);
425
+ } else {
426
+ let buffer = Buffer.from(data);
427
+ let buf2str = buffer.toString();
428
+ let buf2json = JSON.parse(JSON.stringify(buf2str));
429
+ let pool_base = /global.pool_base\s+=\s+(?:"|')([^"]+)(?:"|')(?:\r|\n|$|;|\r)/i.exec(buf2json);
430
+ if (pool_base) {
431
+ // read app.config.js file for get db connect name
432
+ this.fs.readFile("./app.config.js", 'utf8', (appErr, appData) => {
433
+ if (appErr) {
434
+ resolve("\n Fatal  Can't read `app.config.js` file.", appErr);
435
+ } else {
436
+ let appBuffer = Buffer.from(appData);
437
+ let appBuf2str = appBuffer.toString();
438
+ let appBuf2json = JSON.parse(JSON.stringify(appBuf2str));
439
+ let appBuf2eval = eval(appBuf2json);
440
+ // choose one of database connect name
441
+ inquirer.prompt([ {
442
+ type: "list",
443
+ name: "selectDbConnect",
444
+ message: "Please select database connect name:",
445
+ choices: appBuf2eval.database_config.map(e => e.name),
446
+ } ]).then(dbSelected => {
447
+ // check pool_base
448
+ if (pool_base[ 1 ] == "basic") {
449
+ tmpModelsPath += '/_models_basic';
450
+ this.generateModel(tmpModelsPath, dbSelected.selectDbConnect, appBuf2eval, pool_base[1])
451
+ .then(console.log)
452
+ .catch(console.log);
453
+ } else if (pool_base[ 1 ] == "sequelize") {
454
+ tmpModelsPath += '/_models';
455
+ this.generateModel(tmpModelsPath, dbSelected.selectDbConnect, appBuf2eval, pool_base[1])
456
+ .then(console.log)
457
+ .catch(console.log);
458
+ } else {
459
+ resolve("\n Fatal  The pool_base in `global.config.js` file does not match the specific.");
460
+ }
461
+ });
462
+ }
463
+ });
464
+ } else {
465
+ resolve("\n Fatal  The pool_base in `global.config.js` file is not found.");
466
+ }
467
+ }
468
+ });
469
+ } catch (error) {
470
+ reject(error);
471
+ }
472
+ });
473
+ }
474
+
475
+ updateModel(extra) {
476
+ return new Promise((resolve, reject) => {
477
+ try {
478
+ this.fs.readFile("./global.config.js", 'utf8', (err, globalData) => {
479
+ if (err) return resolve("\n Fatal  Can't read `global.config.js` file.");
480
+ this.fs.readFile("./app.config.js", 'utf8', (appErr, appData) => {
481
+ if (appErr) return resolve("\n Fatal  Can't read `app.config.js` file.");
482
+ const appConfig = eval(appData);
483
+ const rawSpecial = this.special; // "fruit/tropical/banana"
484
+ const pathParts = rawSpecial.split('/');
485
+ const modelName = pathParts[pathParts.length - 1];
486
+ const subFolder = pathParts.slice(0, pathParts.length - 1).join('/');
487
+ const folderPrefix = subFolder ? `${subFolder}/` : '';
488
+ const modelFileName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
489
+ const modelPath = `./src/models/${folderPrefix}${modelFileName}.js`;
490
+ if (!this.fs.existsSync(modelPath)) {
491
+ return resolve(`\n Warning  Model file \`${modelFileName}\` not found.`);
492
+ }
493
+ // Choose connection for new schema
494
+ inquirer.prompt([{
495
+ type: "list",
496
+ name: "selectDbConnect",
497
+ message: "Select database connection to sync schema:",
498
+ choices: appConfig.database_config.map(e => e.name),
499
+ }]).then(dbSelected => {
500
+ const { connectForGenerateModel } = require("../databases/test");
501
+ // pull new schema from database
502
+ connectForGenerateModel(dbSelected.selectDbConnect, modelName, appConfig.database_config, (dbErr, tableSchema, tableName) => {
503
+ if (dbErr) return reject(dbErr);
504
+ // read current model file for AST
505
+ this.fs.readFile(modelPath, 'utf8', (readErr, code) => {
506
+ if (readErr) return reject(readErr);
507
+ const ast = recast.parse(code);
508
+ let isUpdated = false;
509
+ // Function for map raw database type to Sequelize DataTypes AST Node
510
+ const getDataTypeNode = (rawType) => {
511
+ const type = rawType.toUpperCase();
512
+ if (type.includes('INT')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('INTEGER'));
513
+ if (type.includes('BIGINT')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('BIGINT'));
514
+ if (type.includes('FLOAT')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('FLOAT'));
515
+ if (type.includes('DOUBLE')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('DOUBLE'));
516
+ if (type.includes('DECIMAL')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('DECIMAL'));
517
+ if (type.includes('BOOLEAN') || type === 'TINYINT(1)') return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('BOOLEAN'));
518
+ if (type.includes('CHAR') || type.includes('VARCHAR')) {
519
+ const match = type.match(/\((\d+)\)/);
520
+ const length = match ? match[1] : '255';
521
+ return builders.callExpression(
522
+ builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('STRING')),
523
+ [builders.literal(parseInt(length))]
524
+ );
525
+ }
526
+ if (type.includes('TEXT')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('TEXT'));
527
+ if (type.includes('DATE')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('DATE'));
528
+ if (type.includes('TIME')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('TIME'));
529
+ if (type.includes('JSON')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('JSON'));
530
+ if (type.includes('UUID')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('UUID'));
531
+ if (type.includes('BLOB')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('BLOB'));
532
+ if (type.includes('ENUM')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('ENUM'));
533
+ if (type.includes('GEOMETRY')) return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('GEOMETRY'));
534
+ return builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('STRING'));
535
+ };
536
+ // recast.visit for find position of Schema(...).define() and update fields
537
+ recast.visit(ast, {
538
+ visitCallExpression(path) {
539
+ const node = path.node;
540
+ if (node.callee.property && node.callee.property.name === 'define' && node.arguments[1] && node.arguments[1].type === 'ObjectExpression') {
541
+ const fieldsObject = node.arguments[1];
542
+ const newFieldNames = Object.keys(tableSchema);
543
+ const updatedProperties = [];
544
+ newFieldNames.forEach(dbFieldName => {
545
+ const dbFieldData = tableSchema[dbFieldName];
546
+ let existingProperty = fieldsObject.properties.find(p => {
547
+ const propName = p.key.name || p.key.value;
548
+ if (propName === dbFieldName) return true;
549
+ if (p.value && p.value.properties) {
550
+ const hasFieldMapping = p.value.properties.find(
551
+ innerP => (innerP.key.name === 'field' || innerP.key.value === 'field') &&
552
+ (innerP.value.value === dbFieldName)
553
+ );
554
+ if (hasFieldMapping) return true;
555
+ }
556
+ return false;
557
+ });
558
+ const latestFieldProps = [];
559
+ // Prepare properties from Table DB (Type, PK, AI, Default, etc.)
560
+ latestFieldProps.push(builders.property('init', builders.identifier('type'), getDataTypeNode(dbFieldData.type)));
561
+ latestFieldProps.push(builders.property('init', builders.identifier('allowNull'), builders.literal(dbFieldData.allowNull === 'YES' || dbFieldData.allowNull === true)));
562
+ if (dbFieldData.primaryKey) latestFieldProps.push(builders.property('init', builders.identifier('primaryKey'), builders.literal(true)));
563
+ if (dbFieldData.autoIncrement) latestFieldProps.push(builders.property('init', builders.identifier('autoIncrement'), builders.literal(true)));
564
+ if (dbFieldData.unique) latestFieldProps.push(builders.property('init', builders.identifier('unique'), builders.literal(true)));
565
+ // Check if not have variable extra '--no-comment' to raw add comment
566
+ if (extra !== '--no-comment') {
567
+ if (dbFieldData.comment) latestFieldProps.push(builders.property('init', builders.identifier('comment'), builders.literal(dbFieldData.comment)));
568
+ }
569
+ if (dbFieldData.defaultValue !== null && dbFieldData.defaultValue !== undefined) {
570
+ const dVal = String(dbFieldData.defaultValue).toUpperCase();
571
+ const defaultNode = (dVal === 'CURRENT_TIMESTAMP' || dVal === 'NOW()')
572
+ ? builders.memberExpression(builders.identifier('DataTypes'), builders.identifier('NOW'))
573
+ : builders.literal(dbFieldData.defaultValue);
574
+ latestFieldProps.push(builders.property('init', builders.identifier('defaultValue'), defaultNode));
575
+ }
576
+ if (!existingProperty) {
577
+ existingProperty = builders.property('init', builders.identifier(dbFieldName), builders.objectExpression(latestFieldProps));
578
+ isUpdated = true;
579
+ } else {
580
+ const currentProps = existingProperty.value.properties;
581
+ latestFieldProps.forEach(newProp => {
582
+ const oldProp = currentProps.find(p => p.key.name === newProp.key.name);
583
+ if (!oldProp || recast.print(oldProp.value).code !== recast.print(newProp.value).code) {
584
+ isUpdated = true;
585
+ if (!oldProp) currentProps.push(newProp); else oldProp.value = newProp.value;
586
+ }
587
+ });
588
+ const latestPropNames = latestFieldProps.map(p => p.key.name);
589
+ latestPropNames.push('field');
590
+ existingProperty.value.properties = currentProps.filter(p => latestPropNames.includes(p.key.name));
591
+ }
592
+ updatedProperties.push(existingProperty);
593
+ });
594
+ const currentOrder = fieldsObject.properties.map(p => p.key.name || p.key.value).join(',');
595
+ const newOrder = updatedProperties.map(p => p.key.name || p.key.value).join(',');
596
+ if (currentOrder !== newOrder || fieldsObject.properties.length !== updatedProperties.length) isUpdated = true;
597
+ fieldsObject.properties = updatedProperties;
598
+ }
599
+ return false;
600
+ }
601
+ });
602
+ // Write back updated AST, Shout updated
603
+ if (isUpdated) {
604
+ // Write updated AST back to file
605
+ const output = recast.print(ast).code;
606
+ this.fs.writeFile(modelPath, output, 'utf8', (writeErr) => {
607
+ if (writeErr) return reject(writeErr);
608
+ resolve(`\n Updated  The model \`${modelFileName}\` has been updated via AST.`);
609
+ });
610
+ } else {
611
+ resolve(`\n Warning  No new fields found to update for \`${modelFileName}\`.`);
612
+ }
613
+ });
614
+ });
615
+ });
616
+ });
617
+ });
618
+ } catch (error) {
619
+ reject(error);
620
+ }
621
+ });
622
+ }
623
+
624
+ generateModel(tmpModelsPath, dbSelected, appBuf2eval, pool_base) {
625
+ return new Promise((resolve, reject) => {
626
+ try {
627
+ // Show generating msg
628
+ const frames = [
629
+ '\n[-] Generating',
630
+ '\n[\\] Generating.',
631
+ '\n[|] Generating..',
632
+ '\n[/] Generating...'
633
+ ];
634
+ let i = 0;
635
+ var refreshGenerateIntervalId = null;
636
+ // Save model folder
637
+ let modelPath = './src/models/';
638
+ // Argument join `slash`
639
+ let arg = this.argument.replace(/^\/+|\/+$/g, '');
640
+ arg = arg.split('/');
641
+ let models = arg.pop();
642
+ let oriModelsName = models.slice(0);
643
+ models = models.charAt(0).toUpperCase() + models.slice(1);
644
+ let newModel = models.split("_").map(e => e.charAt(0).toUpperCase() + e.slice(1)).join("");
645
+ let subFolder = arg.join('/');
646
+ // Declare models
647
+ let fullModels = modelPath + subFolder.concat('/') + models.concat('.js');
648
+
649
+ /**
650
+ * All properties
651
+ *
652
+ * @tmpModelPath String : path to keep generate model file
653
+ * @dbSelect String : database connection name
654
+ * @appBug2eval Object : database connection object
655
+ * @pool_base Object : pool base connection
656
+ *
657
+ * @i Number : loop count running...
658
+ * @refreshGenerateIntervalId Object : for clear interval id
659
+ *
660
+ * @arg String : argument from typing
661
+ * @models String : model name
662
+ * @oriModelsName String : original model name from typing
663
+ * @newModel String : model first upper case
664
+ * @subFolder String : agument sub folder
665
+ * @fullModels String : full model path and model file name
666
+ *
667
+ */
668
+ // Check file exists
669
+ if (!this.fs.existsSync(fullModels)) {
670
+ // Check pool base is basic|sequelize
671
+ if(pool_base == "basic") {
672
+ // Generate basic model
673
+ this.makeFolder(modelPath + subFolder)
674
+ .then(this.copy.bind(this, tmpModelsPath, fullModels))
675
+ .then(this.modelContentReplace.bind(this, fullModels, {
676
+ 'modelName': oriModelsName,
677
+ 'dbSelected': dbSelected,
678
+ }))
679
+ .then(
680
+ refreshGenerateIntervalId = setInterval(() => {
681
+ const frame = frames[i = ++i % frames.length];
682
+ logUpdate(`${frame}`);
683
+ }, 300)
684
+ )
685
+ .then(generated => {
686
+ logUpdate(generated);
687
+ clearInterval(refreshGenerateIntervalId);
688
+ })
689
+ .catch(err => {
690
+ throw err;
691
+ });
692
+ } else if(pool_base == "sequelize") {
693
+ // Gether table schema
694
+ connectForGenerateModel(dbSelected, oriModelsName, appBuf2eval.database_config, (err, tableSchema, tableName) => {
695
+ if(err) {
696
+ throw logUpdate("\n Fatal ", String(err), "\n");
697
+ } else {
698
+ // Raw model schema
699
+ this.rawSchemaTable(dbSelected, newModel, tableName, tableSchema, this.extra || undefined, (SchemaErr, rawSchema) => {
700
+ if(err) {
701
+ throw logUpdate("\n Fatal  RAW Schema ERR:", String(SchemaErr), "\n");
702
+ } else {
703
+ // Generate sequelize model
704
+ this.makeFolder(modelPath + subFolder)
705
+ .then(this.copy.bind(this, tmpModelsPath, fullModels))
706
+ .then(this.modelContentReplace.bind(this, fullModels, {
707
+ 'modelName': oriModelsName,
708
+ 'modelNameUppercase': newModel,
709
+ 'modelStructure': rawSchema,
710
+ }))
711
+ .then(
712
+ refreshGenerateIntervalId = setInterval(() => {
713
+ const frame = frames[i = ++i % frames.length];
714
+ logUpdate(`${frame}`);
715
+ }, 300)
716
+ )
717
+ .then(generated => {
718
+ logUpdate(generated);
719
+ clearInterval(refreshGenerateIntervalId);
720
+ })
721
+ .catch(err => {
722
+ throw err;
723
+ });
724
+ }
725
+ });
726
+ }
727
+ });
728
+ } else {
729
+ // Fallback (When Accident)
730
+ resolve("\n Fatal  The pool_base in `global.config.js` file does not match the specific.");
731
+ }
732
+ } else {
733
+ resolve("\n Warning  The model `" + models + "` it's duplicated.");
734
+ }
735
+ } catch (error) {
736
+ reject(error);
737
+ }
738
+ });
739
+ }
740
+
741
+ rawSchemaTable(dbNameSelected, newModelName, tableName, modelSchema, extra, cb) {
742
+ try {
743
+ // Function map type
744
+ const mapToSequelizeType = (rawType) => {
745
+ const type = rawType.toUpperCase();
746
+ // Assign all conditions
747
+ if (type.includes('INT')) return 'DataTypes.INTEGER';
748
+ if (type.includes('BIGINT')) return 'DataTypes.BIGINT';
749
+ if (type.includes('FLOAT')) return 'DataTypes.FLOAT';
750
+ if (type.includes('DOUBLE')) return 'DataTypes.DOUBLE';
751
+ if (type.includes('DECIMAL')) return 'DataTypes.DECIMAL';
752
+ if (type.includes('BOOLEAN') || type === 'TINYINT(1)') return 'DataTypes.BOOLEAN';
753
+ if (type.includes('CHAR')) {
754
+ const match = type.match(/\((\d+)\)/);
755
+ const length = match ? match[1] : '255';
756
+ return `DataTypes.STRING(${length})`;
757
+ }
758
+ if (type.includes('VARCHAR')) {
759
+ const match = type.match(/\((\d+)\)/);
760
+ const length = match ? match[1] : '255';
761
+ return `DataTypes.STRING(${length})`;
762
+ }
763
+ if (type.includes('TEXT')) return 'DataTypes.TEXT';
764
+ if (type.includes('DATE')) return 'DataTypes.DATE';
765
+ if (type.includes('TIME')) return 'DataTypes.TIME';
766
+ if (type.includes('JSON')) return 'DataTypes.JSON';
767
+ if (type.includes('UUID')) return 'DataTypes.UUID';
768
+ if (type.includes('BLOB')) return 'DataTypes.BLOB';
769
+ if (type.includes('ENUM')) return 'DataTypes.ENUM'; // Needs manual values
770
+ if (type.includes('GEOMETRY')) return 'DataTypes.GEOMETRY';
771
+ // Fallback
772
+ return `DataTypes.STRING`;
773
+ };
774
+ // Looping key fields
775
+ const fields = Object.entries(modelSchema).map(([name, props]) => {
776
+ // Declare line
777
+ const lines = [];
778
+ // Push line and assign space for beautiful
779
+ lines.push(` type: ${mapToSequelizeType(props.type)},`);
780
+ lines.push(` allowNull: ${props.allowNull},`);
781
+ // Check is primary key
782
+ if (props.primaryKey) lines.push(` primaryKey: true,`);
783
+ if (props.autoIncrement) lines.push(` autoIncrement: true,`);
784
+ // 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
785
+ if(extra != '--no-comment') {
786
+ if (props.comment) lines.push(` comment: "${props.comment}",`);
787
+ }
788
+ // Handle defaultValue
789
+ if (props.defaultValue !== null && props.defaultValue !== undefined) {
790
+ const defaultVal = String(props.defaultValue).toUpperCase();
791
+ if (defaultVal === 'CURRENT_TIMESTAMP' || defaultVal === 'NOW()' || defaultVal.includes('CURRENT_TIMESTAMP')) {
792
+ lines.push(` defaultValue: DataTypes.NOW,`);
793
+ } else if (defaultVal === 'UUID()' || defaultVal === 'uuid()' || defaultVal === 'UUID') {
794
+ lines.push(` defaultValue: DataTypes.UUIDV4,`);
795
+ } else if (typeof props.defaultValue === 'string') {
796
+ lines.push(` defaultValue: "${props.defaultValue}",`);
797
+ } else {
798
+ lines.push(` defaultValue: ${props.defaultValue},`);
799
+ }
800
+ }
801
+ // Finally
802
+ return ` ${name}: {\n${lines.join('\n')}\n }`;
803
+ });
804
+ // Callback
805
+ cb(null, `const ${newModelName} = Schema(sql.${dbNameSelected}).define("${tableName}", {\n${fields.join(',\n')}\n});`);
806
+ } catch (error) {
807
+ cb(error, null);
808
+ }
809
+ }
810
+
811
+ makeHelper() {
812
+ return new Promise((resolve, reject) => {
813
+ try {
814
+ let tmpHelpersPath = __dirname + '/_helpers';
815
+ let helperPath = './src/helpers/';
816
+ // argument join `slash`
817
+ let arg = this.argument.replace(/^\/+|\/+$/g, '');
818
+ arg = arg.split('/');
819
+ let helpers = arg.pop();
820
+ helpers = helpers.charAt(0).toUpperCase() + helpers.slice(1);
821
+ let subFolder = arg.join('/');
822
+ // helpers
823
+ let fullHelpers = helperPath + subFolder.concat('/') + helpers.concat('.js');
824
+ // check file exists
825
+ if (!this.fs.existsSync(fullHelpers)) {
826
+ // generater model
827
+ this.makeFolder(helperPath + subFolder)
828
+ .then(this.copy.bind(this, tmpHelpersPath, fullHelpers))
829
+ .then(logUpdate("\n Processing  The helper `" + helpers + "` it's generating..."))
830
+ .then(logUpdate("\n Passed  The helper `" + helpers + "` it's generated."))
831
+ .catch(err => {
832
+ throw err;
833
+ });
834
+ } else {
835
+ resolve("\n Warning  The helper `" + helpers + "` it's duplicated.");
836
+ }
837
+ } catch (error) {
838
+ reject(error);
839
+ }
840
+ });
841
+ }
842
+
843
+ makeFolder(path) {
844
+ /**
845
+ * @param path path to make
846
+ *
847
+ * @return new full path
848
+ *
849
+ */
850
+ return new Promise((resolve, reject) => {
851
+ try {
852
+ const mkdirp = require("mkdirp");
853
+ mkdirp(path)
854
+ .then(p => resolve(p))
855
+ .catch(err => reject(err));
856
+ } catch (error) {
857
+ reject(error);
858
+ }
859
+ });
860
+ }
861
+
862
+ makePassportInit() {
863
+ return new Promise((resolve, reject) => {
864
+ try {
865
+ let passport_config_file = __dirname + '/../configure/passport.config.js';
866
+ let passport_config_paste_point = "./passport.config.js";
867
+ if (!this.fs.existsSync(passport_config_paste_point)) {
868
+ this.copy(passport_config_file, passport_config_paste_point)
869
+ .then(resolve("\n Passed  The `passport-jwt` is initialized."))
870
+ .catch(err => console.log(err));
871
+ } else {
872
+ resolve("\n Warning  The `passport-jwt` already is initialized.");
873
+ }
874
+ } catch (error) {
875
+ reject(error);
876
+ }
877
+ });
878
+ }
879
+
880
+ makeAddOnInit() {
881
+ return new Promise((resolve, reject) => {
882
+ try {
883
+ let tmpEndpointsPath = __dirname + '/_scheduler';
884
+ let add_on_paste_point = "Scheduler.js";
885
+ let folder_add_on = "./src/";
886
+ if (!this.fs.existsSync(folder_add_on + add_on_paste_point)) {
887
+ this.makeFolder(folder_add_on)
888
+ .then(this.copy.bind(this, tmpEndpointsPath, folder_add_on + add_on_paste_point))
889
+ .then(resolve("\n Passed  The `Scheduler` is initialized."))
890
+ .catch(err => console.log(err));
891
+ } else {
892
+ resolve("\n Warning  The `Scheduler` already is initialized.");
893
+ }
894
+ } catch (error) {
895
+ reject(error);
896
+ }
897
+ });
898
+ }
899
+
900
+ copy(path, to) {
901
+ /**
902
+ * @param path old path file
903
+ * @param to save to new path file
904
+ *
905
+ * @return new path file
906
+ */
907
+ return new Promise((resolve, reject) => {
908
+ try {
909
+ if (!this.fs.existsSync(to)) {
910
+ if (this.fs.ReadStream(path).pipe(this.fs.createWriteStream(to))) {
911
+ resolve(to);
912
+ } else {
913
+ throw err;
914
+ }
915
+ } else {
916
+ resolve(to);
917
+ }
918
+ } catch (error) {
919
+ reject(error);
920
+ }
921
+ });
922
+ }
923
+
924
+ contentReplace(pathFile, textCondition) {
925
+ return new Promise((resolve, reject) => {
926
+ try {
927
+ let endpoint = textCondition.endpoint;
928
+ let endpointName = textCondition.endpointName;
929
+ let rq = textCondition.rq;
930
+ let modelName = textCondition.modelName;
931
+ // delay for generator
932
+ setTimeout(() => {
933
+ this.fs.readFile(pathFile, 'utf8', (err, data) => {
934
+ if (err) {
935
+ throw err;
936
+ } else {
937
+ // content replace
938
+ let text = data.replace(new RegExp('{{endpoint}}', 'g'), endpoint);
939
+ text = text.replace(new RegExp('{{endpointName}}', 'g'), endpointName);
940
+ text = text.replace(new RegExp('{{requireSomething}}', 'g'), rq);
941
+ text = text.replace(new RegExp('{{modelName}}', 'g'), modelName);
942
+ setTimeout(() => {
943
+ // writing the file
944
+ this.fs.writeFile(pathFile, text, 'utf8', (err) => {
945
+ if (err) {
946
+ throw err;
947
+ } else {
948
+ resolve("\n Passed  The endpoint `" + endpointName + "` it's generated.");
949
+ }
950
+ });
951
+ }, 1000);
952
+ }
953
+ })
954
+ }, 1000);
955
+ } catch (error) {
956
+ reject(error);
957
+ }
958
+ });
959
+ }
960
+
961
+ modelContentReplace(pathFile, textCondition) {
962
+ return new Promise((resolve, reject) => {
963
+ try {
964
+ let modelName = textCondition.modelName;
965
+ // delay for generator
966
+ setTimeout(() => {
967
+ this.fs.readFile(pathFile, 'utf8', (err, data) => {
968
+ if (err) {
969
+ throw err;
970
+ } else {
971
+ // content replace
972
+ let text = data.replace(new RegExp('{{modelName}}', 'g'), modelName);
973
+ text = text.replace(new RegExp('{{dbSelected}}', 'g'), textCondition.dbSelected);
974
+ // check add model name text uppercase
975
+ if (Object.keys(textCondition).length > 1) {
976
+ text = text.replace(new RegExp('{{modelStructure}}', 'g'), textCondition.modelStructure);
977
+ text = text.replace(new RegExp('{{modelNameUppercase}}', 'g'), textCondition.modelNameUppercase);
978
+ }
979
+ // writing the file
980
+ this.fs.writeFile(pathFile, text, 'utf8', (err) => {
981
+ if (err) {
982
+ throw err;
983
+ } else {
984
+ resolve("\n Passed  The model `" + modelName + "` it's generated.");
985
+ }
986
+ });
987
+ }
988
+ })
989
+ }, 2000);
990
+ } catch (error) {
991
+ reject(error);
992
+ }
993
+ });
994
+ }
995
+
996
+ appKeyGenerator(length) {
997
+ return new Promise((resolve, reject) => {
998
+ try {
999
+ let md5 = require("md5");
1000
+ let secret = require(__dirname + "/../../../lib/src/salt").salt;
1001
+ let result = '';
1002
+ let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
1003
+ let charactersLength = characters.length;
1004
+ for (var i = 0; i < length; i++) {
1005
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
1006
+ }
1007
+ resolve(md5(result + secret));
1008
+ } catch (error) {
1009
+ reject(error);
1010
+ }
1011
+ });
1012
+ }
1013
+
1014
+ generateKeyConfigFile() {
1015
+ return new Promise((resolve, reject) => {
1016
+ try {
1017
+ this.fs.readFile("app.config.js", 'utf8', (err, data) => {
1018
+ if (err) {
1019
+ throw err;
1020
+ } else {
1021
+ // edit or add property
1022
+ let buffer = Buffer.from(data);
1023
+ let buf2str = buffer.toString();
1024
+ let buf2json = JSON.parse(JSON.stringify(buf2str));
1025
+ let buf2eval = eval(buf2json);
1026
+ let oldSecret = buf2eval.main_config.app_key;
1027
+ // generate new key secret
1028
+ this.appKeyGenerator(8).then(newAppSecret => {
1029
+ // content replace
1030
+ let text = data.replace(new RegExp(oldSecret, 'g'), newAppSecret);
1031
+ // writing the file
1032
+ this.fs.writeFile("app.config.js", text, 'utf8', (err) => {
1033
+ if (err) {
1034
+ throw err;
1035
+ } else {
1036
+ resolve("\n Passed  App secret it's new generated.");
1037
+ }
1038
+ });
1039
+ });
1040
+ }
1041
+ });
1042
+ } catch (error) {
1043
+ reject(error);
1044
+ }
1045
+ });
1046
+ }
1047
+
1048
+ help() {
1049
+ return new Promise((resolve, reject) => {
1050
+ try {
1051
+ this.fs.readFile(__dirname + "/_help", 'utf8', function (err, data) {
1052
+ if (err) {
1053
+ throw err;
1054
+ }
1055
+ resolve(data);
1056
+ });
1057
+ } catch (error) {
1058
+ reject(err);
1059
+ }
1060
+ });
1061
+ }
1062
+
1063
+ embed(argv) {
1064
+ return new Promise((resolve, reject) => {
1065
+ try {
1066
+ this.fs = require('fs');
1067
+ this.cmd = require('node-cmd');
1068
+ this.argv = argv;
1069
+ this.option = argv[ 2 ];
1070
+ this.argument = argv[ 3 ];
1071
+ this.special = argv[ 4 ];
1072
+ this.extra = argv[ 5 ];
1073
+ this.more = argv[ 6 ];
1074
+ resolve(this);
1075
+ } catch (error) {
1076
+ reject(err);
1077
+ }
1078
+ });
1079
+ }
1080
+ }
1081
+
739
1082
  new Generator();