beech-api 3.3.1 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -259,6 +259,17 @@ You can easy management `users` data with Beech helper just define below:
259
259
  });
260
260
  ```
261
261
 
262
+ ## API with Official strategies
263
+
264
+
265
+
266
+
267
+
268
+
269
+
270
+
271
+
272
+
262
273
  ## Databases managements
263
274
 
264
275
  ### # Migrations & Seeder
@@ -312,6 +323,8 @@ Before continuing further we will need to tell CLI how to connect to database. T
312
323
  }
313
324
  ```
314
325
 
326
+ :grey_question: **Note:** The database connect default port 3306 if you another port you can add object ``port`` in config.
327
+
315
328
  :grey_question: **Note:** If your database doesn't exists yet, you can just call `npx sequelize-cli db:create` command. With proper access it will create that database for you.
316
329
 
317
330
  ### # Creating first Migrations
@@ -409,6 +422,71 @@ describe("Test endpoint : " + endpoint, () => {
409
422
  });
410
423
  ```
411
424
 
425
+ ## Implementation
426
+
427
+ ### # Implement with [PM2](https://pm2.keymetrics.io/)
428
+ [PM2](https://pm2.keymetrics.io/) is a daemon process manager that will help you manage and keep your application online. Getting started with PM2 is straightforward, it is offered as a simple and intuitive CLI, installable via [NPM](https://www.npmjs.com/).
429
+
430
+ ```sh
431
+ # Start service as standalone
432
+ $ pm2 start ./node_modules/beech-api/packages/cli/beech --name <serviceName>
433
+
434
+ # OR
435
+
436
+ # Start service as cluster mode
437
+ $ pm2 start ./node_modules/beech-api/packages/cli/beech --name <serviceName> -i <instances>
438
+ ```
439
+
440
+ ### # Implement with [Docker](https://www.docker.com)
441
+
442
+ [Docker](https://www.docker.com) is an open platform for developing, shipping, and running applications. Docker enables you to separate your applications from your infrastructure so you can deliver software quickly.
443
+
444
+ - **Create Dockerfile**
445
+
446
+ Docker builds images automatically by reading the instructions from a Dockerfile -- a text file that contains all commands, in order, needed to build a given image. A Dockerfile adheres to a specific format and set of instructions which you can find at [Dockerfile reference](https://docs.docker.com/engine/reference/builder/).
447
+
448
+ ```js
449
+ // Dockerfile
450
+
451
+ FROM node:12.18-alpine
452
+ ENV NODE_ENV=production
453
+ WORKDIR /usr/src/api
454
+ COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
455
+ RUN npm install --production --silent && mv node_modules .
456
+ COPY . .
457
+ EXPOSE 9000
458
+ CMD ["node", "./node_modules/beech-api/packages/cli/beech"]
459
+ ```
460
+
461
+ - **Docker build image**
462
+
463
+ The docker build command builds an image from a Dockerfile and a context. The build’s context is the set of files at a specified location ```PATH``` or ```URL```. The PATH is a directory on your local filesystem. The URL is a Git repository location.
464
+
465
+ ```sh
466
+ $ docker build -t <imageName> .
467
+ ```
468
+
469
+ :grey_question: Tips: You can specify a repository and tag at which to save the new image : ``` $ docker build -t <imageName>:<tags> . ``` |
470
+ ------------ |
471
+
472
+ - **Run docker**
473
+
474
+ After create ``image`` you can run docker engine following :
475
+
476
+ - **Docker Container (Standalone)**
477
+ ```sh
478
+ $ docker run -d -p 9000:9000 --name <containerName> <imageName>
479
+ ```
480
+
481
+ - **Create Docker Swarm (Cluster)**
482
+ ```sh
483
+ # initiate swarm
484
+ $ docker swarm init
485
+ # run docker service
486
+ $ docker service create --replicas <instances> --name <containerName> --publish 9000:9000 <imageName>
487
+ ```
488
+
489
+
412
490
  ## Bonus
413
491
 
414
492
  Free `helpers` you can make [LINE Notify](https://github.com/Yuhsak/line-api#readme) by using [line-api](https://notify-bot.line.me/en/) package with create the helper function following.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beech-api",
3
- "version": "3.3.1",
3
+ "version": "3.4.0",
4
4
  "description": "Command line interface for rapid Beech API development",
5
5
  "keywords": [
6
6
  "beech-api",
@@ -26,7 +26,7 @@
26
26
  "homepage": "https://github.com/bombkiml/beech-api/blob/master/README.md",
27
27
  "deprecated": false,
28
28
  "engines": {
29
- "node": ">=10.13"
29
+ "node": ">=8.9"
30
30
  },
31
31
  "dependencies": {
32
32
  "fs": "0.0.1-security",
@@ -36,12 +36,26 @@
36
36
  "mkdirp": "^0.5.1",
37
37
  "node-cmd": "^3.0.0",
38
38
  "passport": "^0.4.1",
39
+ "passport-facebook": "^3.0.0",
40
+ "passport-google-oauth": "^2.0.0",
39
41
  "passport-jwt": "^4.0.0",
40
42
  "passport-local": "^1.0.0",
41
- "path": "^0.12.7"
43
+ "passport-oauth": "^1.0.0",
44
+ "path": "^0.12.7",
45
+ "cookie-parser": "1.4.3",
46
+ "cors": "^2.8.1",
47
+ "express": "4.16.3",
48
+ "express-session": "^1.17.1",
49
+ "express-validator": "2.21.0",
50
+ "module-alias": "^2.2.2",
51
+ "mysql": "^2.18.1",
52
+ "sequelize": "^5.21.6",
53
+ "walk": "^2.3.14",
54
+ "app-root-path": "^3.0.0"
42
55
  },
43
56
  "devDependencies": {
44
- "jest": "^25.2.7"
57
+ "jest": "^25.2.7",
58
+ "sequelize-cli": "^5.5.1"
45
59
  },
46
60
  "jest": {
47
61
  "prdFile": "./node_modules/beech-api/packages/cli/core/test/utils.js",
@@ -8,8 +8,13 @@ module.exports = {
8
8
  init() {
9
9
  try {
10
10
  if (fs.existsSync(passport_config_file)) {
11
- var passport = require("passport"), LocalStrategy = require("passport-local").Strategy;
12
- var passportJWT = require("passport-jwt"), JWTStrategy = passportJWT.Strategy, ExtractJWT = passportJWT.ExtractJwt;
11
+ var passport = require("passport"),
12
+ LocalStrategy = require("passport-local").Strategy,
13
+ GoogleStrategy = require("passport-google-oauth").OAuth2Strategy,
14
+ FacebookStrategy = require('passport-facebook').Strategy;
15
+ var passportJWT = require("passport-jwt"),
16
+ JWTStrategy = passportJWT.Strategy,
17
+ ExtractJWT = passportJWT.ExtractJwt;
13
18
  const auth = require("./Credentials");
14
19
  var passport_config = require(passport_config_file);
15
20
  if (passport_config.jwt_allow) {
@@ -22,14 +27,12 @@ module.exports = {
22
27
  global.Credentials = [];
23
28
  return;
24
29
  }
25
- // passport initialization
26
- _app_.use(passport.initialize());
27
30
  // declare constant
28
31
  let passportUsernameField = passport_config.model.username_field || "username";
29
32
  let passportPasswordField = passport_config.model.password_field || "password";
30
33
  let passportTable = passport_config.model.table || "users";
31
34
  let passportFields = (passport_config.model.fields.length) ? passport_config.model.fields : ["id", "name", "email"];
32
- // passport initial with token
35
+ // passport initial with token (encoder)
33
36
  passport.use(new LocalStrategy({
34
37
  usernameField: passportUsernameField,
35
38
  passwordField: passportPasswordField
@@ -44,7 +47,6 @@ module.exports = {
44
47
  md5(password + secret)
45
48
  ], (err, result) => {
46
49
  if (err) {
47
- console.log("pass",err)
48
50
  return done(err);
49
51
  } else {
50
52
  return done(null, JSON.parse(JSON.stringify(result[0] || null)));
@@ -54,7 +56,7 @@ module.exports = {
54
56
  return done(null, null, true);
55
57
  }
56
58
  }));
57
- // passport jwt payload
59
+ // passport jwt payload (decoder)
58
60
  passport.use(new JWTStrategy({
59
61
  jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
60
62
  secretOrKey: passport_config.secret
@@ -75,8 +77,128 @@ module.exports = {
75
77
  return done(null, null, true);
76
78
  }
77
79
  }));
80
+
81
+ // declare head authentication enpoint for all strategy
82
+ let auth_endpoint = (passport_config.auth_endpoint) ? (passport_config.auth_endpoint[0] === "/" ? passport_config.auth_endpoint : "/" + passport_config.auth_endpoint) : "/authentication";
83
+
84
+ /**
85
+ * Passport Google Strategy
86
+ *
87
+ */
88
+ let google_callback_endpoint = (passport_config.strategy.google.callback_endpoint) ? (passport_config.strategy.google.callback_endpoint[0] === "/" ? passport_config.strategy.google.callback_endpoint : "/" + passport_config.strategy.google.callback_endpoint) : "/google/callback";
89
+ passport.use(new GoogleStrategy({
90
+ clientID: passport_config.strategy.google.client_id,
91
+ clientSecret: passport_config.strategy.google.client_secret,
92
+ callbackURL: auth_endpoint + google_callback_endpoint
93
+ }, (accessToken, refreshToken, profile, done) => {
94
+ // find google user
95
+ let googleIdField = (passport_config.strategy.google.google_id_field) ? passport_config.strategy.google.google_id_field : "google_id";
96
+ this.findOrCreate(passport_config, "google", passportFields, passportTable, accessToken, refreshToken, profile, googleIdField, (err, res, dbFailed) => {
97
+ if (err) {
98
+ return done(err);
99
+ } else {
100
+ return done(err, res, dbFailed);
101
+ }
102
+ });
103
+ }));
104
+
105
+ /**
106
+ * Passport Facebook Strategy
107
+ *
108
+ */
109
+ let facebook_callback_endpoint = (passport_config.strategy.facebook.callback_endpoint) ? (passport_config.strategy.facebook.callback_endpoint[0] === "/" ? passport_config.strategy.facebook.callback_endpoint : "/" + passport_config.strategy.facebook.callback_endpoint) : "/facebook/callback";
110
+ passport.use(new FacebookStrategy({
111
+ clientID: passport_config.strategy.facebook.app_id,
112
+ clientSecret: passport_config.strategy.facebook.app_secret,
113
+ callbackURL: auth_endpoint + facebook_callback_endpoint
114
+ }, (accessToken, refreshToken, profile, done) => {
115
+
116
+ console.log(accessToken, refreshToken, profile);
117
+
118
+ // fix somthing went wrong
119
+
120
+
121
+ //return
122
+
123
+ // find facebook user
124
+ let faecbookIdField = (passport_config.strategy.facebook.google_id_field) ? passport_config.strategy.facebook.facebook_id_field : "facebook_id";
125
+ this.findOrCreate(passport_config, "facebook", passportFields, passportTable, accessToken, refreshToken, profile, faecbookIdField, (err, res, dbFailed) => {
126
+ if (err) {
127
+ return done(err);
128
+ } else {
129
+ return done(err, res, dbFailed);
130
+ }
131
+ });
132
+ }
133
+ ));
78
134
  } catch (error) {
79
- return done(true);
135
+ throw error;
136
+ }
137
+ },
138
+ findOrCreate(passport_config, strategy_name, passportFields, passportTable, accessToken, refreshToken, profile, googleIdField, cb) {
139
+ let pool = eval("mysql." + passport_config.model.name);
140
+ if (pool) {
141
+ pool.query("SELECT " + passportFields + " FROM ?? WHERE ?? = ?", [
142
+ passportTable,
143
+ googleIdField,
144
+ profile.id
145
+ ], (err, result) => {
146
+ if (err) {
147
+ cb(err);
148
+ } else {
149
+ // declare data response
150
+ let data = {};
151
+ // check strategy name for store
152
+ if (strategy_name == "google") {
153
+ if (!result[0]) { // find not found and create
154
+ // filter fields
155
+ let fields = [].concat.apply([], [
156
+ (passport_config.strategy.google.profile_fields.name) ? passport_config.strategy.google.profile_fields.name : null,
157
+ (passport_config.strategy.google.profile_fields.email) ? passport_config.strategy.google.profile_fields.email : null,
158
+ (passport_config.strategy.google.profile_fields.photos) ? passport_config.strategy.google.profile_fields.photos : null,
159
+ (passport_config.strategy.google.profile_fields.locate) ? passport_config.strategy.google.profile_fields.locate : null
160
+ ].filter((el) => el != null));
161
+ // fileter values
162
+ let values = [].concat.apply([], [
163
+ (passport_config.strategy.google.profile_fields.name) ? profile.displayName : null,
164
+ (passport_config.strategy.google.profile_fields.email) ? profile.emails[0].value : null,
165
+ (passport_config.strategy.google.profile_fields.photos) ? profile.photos[0].value : null,
166
+ (passport_config.strategy.google.profile_fields.locate) ? profile._json.locale : null
167
+ ].filter((el) => el != null));
168
+ // Store google profile
169
+ pool.query("INSERT INTO ??(??,??,??,??) VALUES(?,?,?,?)", [
170
+ passportTable,
171
+ passport_config.model.username_field || "username",
172
+ passport_config.model.password_field || "password",
173
+ googleIdField,
174
+ fields,
175
+ profile.emails[0].value.split("@")[0],
176
+ md5(profile.id + secret),
177
+ profile.id,
178
+ values
179
+ ], (err, result) => {
180
+ data.result = result;
181
+ data.google = profile;
182
+ cb(err, data);
183
+ });
184
+ } else { // find found
185
+ let users = {};
186
+ users.google = profile;
187
+ users.google.accessToken = accessToken;
188
+ users.google.refreshToken = refreshToken;
189
+ users.user = result[0];
190
+ cb(err, users);
191
+ }
192
+ } else if (strategy_name == "facebook") {
193
+
194
+ console.log(result)
195
+
196
+ }
197
+ }
198
+ });
199
+ } else {
200
+ cb(null, null, true);
80
201
  }
81
202
  }
203
+
82
204
  }
@@ -15,6 +15,9 @@ module.exports = {
15
15
  app_secret: ["2cc118cd91b52ff99e3c005ddced76fb"]
16
16
  },
17
17
 
18
+ // Add-on it's work when enable. You can enable add-on by run CMD `$ beech add-on init`.
19
+ addOn: true,
20
+
18
21
  /**
19
22
  * Database configuration (mutiple connection) currenty support for MySQL
20
23
  *
@@ -1,23 +1,84 @@
1
1
  module.exports = {
2
- // allow using jwt
2
+ // Allow using jwt
3
3
  jwt_allow: true,
4
- // custom authenticaiton endpoint, default `/authentication`
4
+
5
+ // Custom authenticaiton endpoint, default `/authentication`
5
6
  auth_endpoint: "",
6
- // your jwt secret key
7
+
8
+ // Assign your jwt secret key
7
9
  secret: "your_jwt_secret",
8
- // token expiry time (second), default expired in 1 day
10
+
11
+ // Set token expiry time (seconds), default expired in 24 hr.
9
12
  token_expired: 86400,
13
+
10
14
  model: {
11
- // mysql connection name inside `app.config.js` file
15
+ // Main mysql connection same name inside `app.config.js` file
12
16
  name: "default_db",
13
- // table name store your authenticate, default table `users`
17
+ // The user table name for store your authenticate, default table `users`
14
18
  table: "",
15
- // secret field for authenticate, default field `username` and `password`
19
+ // The fields for authenticate, default fields: `username` and `password`
16
20
  username_field: "",
17
21
  password_field: "",
18
- // show fields, default show fields ["id", "name", "email"]
22
+ // Show JWT fields, default show fields: ["id", "name", "email"]
19
23
  fields: []
20
24
  },
21
- // allow using with app_secret requset
22
- app_secret_allow: false
25
+
26
+ // Allow using with app_secret requset
27
+ app_secret_allow: true,
28
+
29
+ // Official strategy
30
+ strategy: {
31
+ /**
32
+ * The Client Id and Client Secret needed to authenticate with Google can be set up from the Google Developers Console (https://console.developers.google.com/)
33
+ * You may also need to enable Google+ API in the developer console, otherwise user profile data may not be fetched.
34
+ * Now Google supports authentication with oAuth 2.0.
35
+ *
36
+ */
37
+ google: {
38
+ // Allow using google strategy
39
+ allow: true,
40
+ // Google ID fields, default field: `google_id`
41
+ google_id_field: "",
42
+ // User profile fields, default fields: `name`, `email`, `photos`, `locate`
43
+ profile_fields: {
44
+ name: "name",
45
+ email: "email",
46
+ photos: "profile_url",
47
+ locate: "" // If not storing set to null
48
+ },
49
+ // Google development Credentials OAuth 2.0 Client IDs
50
+ client_id: GOOGLE_CLIENT_ID,
51
+ client_secret: GOOGLE_CLIENT_SECRET,
52
+ // Callback endpoint default `/google/callback`
53
+ callback_endpoint: ""
54
+ },
55
+
56
+ /**
57
+ * The Facebook strategy allows users to log in to a web application using their Facebook account. Internally, Facebook authentication works using OAuth 2.0.
58
+ * Support for Facebook is implemented by the passport-facebook (https://github.com/jaredhanson/passport-facebook) module.
59
+ *
60
+ * In order to use Facebook authentication, you must first create an app at Facebook Developers. (https://developers.facebook.com/apps) When created, an app is assigned an App ID and App Secret.
61
+ * Your application must also implement a redirect URL, to which Facebook will redirect users after they have approved access for your application.
62
+ *
63
+ */
64
+ facebook: {
65
+ // Allow using facebook strategy
66
+ allow: true,
67
+ // Facebook ID fields, default field: `facebook_id`
68
+ facebook_id_field: "",
69
+ // User profile fields, default fields: `name`, `email`, `photos`, `locate`
70
+ profile_fields: {
71
+ name: "name",
72
+ email: "email",
73
+ photos: "profile_url",
74
+ locate: "" // If not storing set to null
75
+ },
76
+ // Facebook development Credentials OAuth 2.0
77
+ app_id: FACEBOOK_APP_ID,
78
+ app_secret: FACEBOOK_APP_SECRET,
79
+ // Callback endpoint default `/facebook/callback`
80
+ callback_endpoint: ""
81
+ },
82
+
83
+ }
23
84
  }
@@ -2,9 +2,16 @@ exports.mySqlConnection = () => {
2
2
  return new Promise((resolve, reject) => {
3
3
  try {
4
4
  global.mysql = {};
5
+ var headDbShow = true;
5
6
  // loop database connection
6
7
  _config_.mysql_config.map((val, index) => {
7
8
  if (val.is_connect) {
9
+ // show only one text db connnections
10
+ if (headDbShow) {
11
+ console.log('\n Passed  Database is connected at:');
12
+ headDbShow = false;
13
+ }
14
+ // db connection config
8
15
  let connection = _mysql_.createConnection({
9
16
  host: val.host,
10
17
  user: val.username,
@@ -13,10 +20,11 @@ exports.mySqlConnection = () => {
13
20
  charset: val.charset,
14
21
  port: val.port
15
22
  })
23
+ // db connecting
16
24
  connection.connect((err) => {
17
25
  if (!err) {
18
26
  mysql[val.name] = connection;
19
- console.log(' Passed  Database is connected at [' + val.name, '->', connection.config.database + ':' + connection.config.port + ']');
27
+ console.log(' - ' + val.name, '->', connection.config.database + ':' + connection.config.port + '');
20
28
  // checking for resolve
21
29
  if ((index + 1) == _config_.mysql_config.length) {
22
30
  resolve(true);
@@ -0,0 +1,9 @@
1
+ //const Users = require("@/models/Users");
2
+
3
+ exports.init = () => {
4
+
5
+ //Users.findAll().then(console.log)
6
+
7
+ console.log("Add-on is work!")
8
+
9
+ }
@@ -13,4 +13,5 @@ The following commands are available:
13
13
  for require model file(s) in generate processing
14
14
  $ beech make <model> --model Create a new models file
15
15
  $ beech passport init Initialize authentication with passport-jwt
16
+ $ beech add-on init Initialize add-on file
16
17
 
@@ -79,17 +79,25 @@ class Generator {
79
79
  }
80
80
  }
81
81
  } else if (this.option == 'passport') {
82
- if (!this.argument) {
83
- resolve("\n Warning  Using `passport init` for initiate passport-jwt.");
84
- } else {
82
+ if (this.argument == "init") {
85
83
  this.makePassportInit()
86
84
  .then(make => resolve(make))
87
85
  .catch(err => reject(err));
86
+ } else {
87
+ resolve("\n Warning  Using `passport init` for initiate passport-jwt.");
88
88
  }
89
89
  } else if (this.option == "key:generate") {
90
90
  this.generateKeyConfigFile()
91
91
  .then(resGenKey => resolve(resGenKey))
92
92
  .catch(err => reject(err));;
93
+ } else if (this.option == "add-on") {
94
+ if (this.argument == "init") {
95
+ this.makeAddOnInit()
96
+ .then(make => resolve(make))
97
+ .catch(err => reject(err));
98
+ } else {
99
+ resolve("\n Warning  Using `add-on init` for initiate add-on.");
100
+ }
93
101
  } else {
94
102
  resolve("\n Faltal  commnad it's not available.");
95
103
  }
@@ -251,6 +259,26 @@ class Generator {
251
259
  });
252
260
  }
253
261
 
262
+ makeAddOnInit() {
263
+ return new Promise((resolve, reject) => {
264
+ try {
265
+ let tmpEndpointsPath = __dirname + '/add-on';
266
+ let add_on_paste_point = "Add-on.js";
267
+ let folder_add_on = "./src/";
268
+ if (!this.fs.existsSync(folder_add_on + add_on_paste_point)) {
269
+ this.makeFolder(folder_add_on)
270
+ .then(this.copy.bind(this, tmpEndpointsPath, folder_add_on + add_on_paste_point))
271
+ .then(resolve("\n Passed  The `add-on` is initialized."))
272
+ .catch(err => console.log(err));
273
+ } else {
274
+ resolve("\n Warning  The `add-on` already is initialized.");
275
+ }
276
+ } catch (error) {
277
+ reject(error);
278
+ }
279
+ });
280
+ }
281
+
254
282
  copy(path, to) {
255
283
  /**
256
284
  * @param path old path file
@@ -8,23 +8,9 @@
8
8
  "test": "node ./node_modules/jest/bin/jest __tests__ -o --watch --config"
9
9
  },
10
10
  "dependencies": {
11
- "app-root-path": "^3.0.0",
12
- "axios": "^0.18.1",
13
- "beech-api": "^3.2.11",
14
- "cookie-parser": "1.4.3",
15
- "cors": "^2.8.1",
16
- "express": "4.16.3",
17
- "express-validator": "2.21.0",
18
- "fs": "0.0.1-security",
19
- "module-alias": "^2.2.2",
20
- "mysql": "^2.18.1",
21
- "mysql2": "^2.1.0",
22
- "nodemon": "^2.0.2",
23
- "sequelize": "^5.21.6",
24
- "walk": "^2.3.14"
11
+ "beech-api": "^3.2.11"
25
12
  },
26
13
  "devDependencies": {
27
- "jest": "^25.2.7",
28
- "sequelize-cli": "^5.5.1"
14
+ "jest": "^25.2.7"
29
15
  }
30
16
  }
@@ -1,3 +1,4 @@
1
+ const fs = require("fs");
1
2
  const appRoot = require("app-root-path");
2
3
  const moduleAlias = require("module-alias");
3
4
  moduleAlias.addAlias("@", appRoot + "/src");
@@ -8,18 +9,25 @@ global.endpoint = _express_.Router();
8
9
  global._mysql_ = require("mysql");
9
10
  const cookieParser = require("cookie-parser");
10
11
  const bodyParser = require("body-parser");
12
+ const expressSession = require("express-session");
11
13
  const expressValidator = require("express-validator");
12
14
  const globalVariable = require(appRoot + "/global.config.js");
13
15
  globalVariable.init();
14
16
  // Local environments
15
17
  global._config_ = require(appRoot + "/app.config");
16
18
  const dbConnect = require("./databases/mysql.connection");
19
+ dbConnect.mySqlConnection();
17
20
  const httpExpress = require("./services/http.express");
18
21
  const fileWalk = require("./file-walk/file-walk");
19
22
  // View engine
20
23
  _app_.use(bodyParser.json());
21
24
  _app_.use(bodyParser.urlencoded({ extended: true }));
22
25
  _app_.use(cookieParser());
26
+ _app_.use(expressSession({
27
+ secret: 'keyboard cat',
28
+ resave: true,
29
+ saveUninitialized: true
30
+ }));
23
31
  _app_.use(expressValidator());
24
32
  _app_.use(cors({ origin: true, credentials: true }));
25
33
  // Allow Origin
@@ -30,16 +38,32 @@ _app_.all("/", (req, res, next) => {
30
38
  res.header("Content-Type", "application/json; charset=utf-8");
31
39
  next();
32
40
  });
33
- // passport
34
- const passport = require("./auth/Passport");
41
+ // passport initialization
42
+ const authPassport = require("./auth/Passport");
43
+ const passport = require("passport");
44
+ _app_.use(passport.initialize());
45
+ _app_.use(passport.session());
46
+ passport.serializeUser((user, done) =>{
47
+ done(null, user);
48
+ });
49
+ passport.deserializeUser((user, done) => {
50
+ done(null, user);
51
+ });
35
52
  // Read folder in ./src/endpoints/*
36
53
  const walk = require("walk");
37
54
  let jsfiles = [];
38
55
  let walker = walk.walk(appRoot + "/src/endpoints", { followLinks: false });
39
56
  walker.on("file", (root, stat, next) => {
40
57
  jsfiles.push(root + "/" + stat.name);
58
+ // check add-on file exists ?
59
+ if (fs.existsSync(root + "/../Add-on.js")) {
60
+ if (_config_.addOn) {
61
+ jsfiles.push(root + "/../Add-on.js");
62
+ }
63
+ }
41
64
  next();
42
65
  });
66
+ // Walking
43
67
  walker.on("end", () => {
44
68
  init(jsfiles);
45
69
  });
@@ -47,8 +71,7 @@ walker.on("end", () => {
47
71
  init = async (jsfiles) => {
48
72
  try {
49
73
  await httpExpress.expressStart();
50
- await dbConnect.mySqlConnection();
51
- await passport.init();
74
+ await authPassport.init();
52
75
  await fileWalk.fileWalk(jsfiles);
53
76
  } catch (error) {
54
77
  throw error;
@@ -61,8 +61,12 @@ module.exports = {
61
61
  var passport = require('passport');
62
62
  var Beech = require("../../../lib/beech");
63
63
  if (!passport_config.jwt_allow) {
64
+ // jwt is false
65
+ console.log(" - JWT: OFF");
64
66
  return;
65
67
  }
68
+ // jwt is true
69
+ console.log(" - JWT: ON");
66
70
  } else {
67
71
  return;
68
72
  }
@@ -76,16 +80,22 @@ module.exports = {
76
80
  code: 502,
77
81
  error: 'BAD_GATEWAY',
78
82
  message: err
79
- })
83
+ });
80
84
  }
81
85
  if (user) {
82
- const token = jwt.sign(user, passport_config.secret, {
86
+ const accessToken = jwt.sign(user, passport_config.secret, {
83
87
  expiresIn: passport_config.token_expired
84
88
  });
85
- if (passport_config.app_secret_allow) {
89
+ if (passport_config.app_secret_allow) {
86
90
  if (req.body.app_secret) {
87
91
  if (_config_.main_config.app_secret == req.body.app_secret) {
88
- res.status(200).json({ user, token });
92
+ res.status(200).json({
93
+ code: 200,
94
+ status: "AUTHORIZED",
95
+ message: "success.",
96
+ user,
97
+ accessToken
98
+ });
89
99
  } else {
90
100
  res.status(401).json({ code: 401, message: "Unauthorized." });
91
101
  }
@@ -93,7 +103,13 @@ module.exports = {
93
103
  res.status(422).json({ code: 422, message: "Unprocessable Entity." });
94
104
  }
95
105
  } else {
96
- res.status(200).json({ user, token });
106
+ res.status(200).json({
107
+ code: 200,
108
+ status: "AUTHORIZED",
109
+ message: "success.",
110
+ user,
111
+ accessToken
112
+ });
97
113
  }
98
114
  } else if (opt) {
99
115
  res.status(422).json({ code: 422, message: "Unprocessable Entity." });
@@ -104,13 +120,97 @@ module.exports = {
104
120
  });
105
121
  // create users endpoints
106
122
  _app_.post(auth_endpoint + '/users', (req, res) => {
107
- Beech.store(req.body, e => res.status(e.code).json(e));
123
+ Beech.store(req.body, (err, result) => {
124
+ if (err) {
125
+ res.status(500).json({ code: 500, status: "CREATE_FAILED", error: err });
126
+ } else {
127
+ res.status(201).json({ code: 201, status: "CREATE_SUCCESS", result });
128
+ }
129
+ });
108
130
  });
109
131
  // patch users endpoints
110
132
  _app_.patch(auth_endpoint + '/users/:id', auth.credentials, (req, res) => {
111
133
  // require some fields with body params
112
- Beech.update(req.body, req.params.id, e => res.status(e.code).json(e));
134
+ Beech.update(req.body, req.params.id, (err, result) => {
135
+ if (err) {
136
+ res.status(500).json({ code: 500, status: "UPDATE_FAILED", error: err });
137
+ } else {
138
+ res.status(200).json({ code: 200, status: "UPDATE_SUCCESS", result });
139
+ }
140
+ });
113
141
  });
142
+ /**
143
+ * Google Strategy
144
+ *
145
+ */
146
+ if (passport_config.strategy.google.allow) {
147
+ _app_.get(auth_endpoint + '/google', passport.authenticate('google', {
148
+ scope: [
149
+ 'https://www.googleapis.com/auth/userinfo.email',
150
+ 'https://www.googleapis.com/auth/plus.login'
151
+ ]
152
+ }));
153
+ // google auth callback
154
+ const googleCallback = (passport_config.strategy.google.callback_endpoint) ? (passport_config.strategy.google.callback_endpoint[0] === "/" ? passport_config.strategy.google.callback_endpoint : "/" + passport_config.strategy.google.callback_endpoint) : "/google/callback";
155
+ _app_.get(auth_endpoint + googleCallback, passport.authenticate('google'), (req, res) => {
156
+ if (typeof req.user.user !== 'undefined') {
157
+ // declare user for sign JWT
158
+ let user = JSON.parse(JSON.stringify(req.user.user));
159
+ const accessToken = jwt.sign(user, passport_config.secret, {
160
+ expiresIn: passport_config.token_expired
161
+ });
162
+ // response JWT
163
+ res.status(200).json({
164
+ code: 200,
165
+ status: "AUTHORIZED",
166
+ message: "success.",
167
+ user: req.user,
168
+ accessToken
169
+ });
170
+ } else {
171
+ let condUser = {};
172
+ condUser[(passport_config.strategy.google.google_id_field) ? passport_config.strategy.google.google_id_field : "google_id"] = req.user.google.id;
173
+ Beech.findOne(passport_config.model.table || "users", condUser, (err, result) => {
174
+ if (err) {
175
+ res.status(500).json({
176
+ code: 500,
177
+ status: "INTERNAL_SERVER_ERR",
178
+ message: "Internal server error.",
179
+ error: err
180
+ });
181
+ } else {
182
+ let user = JSON.parse(JSON.stringify(result[0]));
183
+ const accessToken = jwt.sign(user, passport_config.secret, {
184
+ expiresIn: passport_config.token_expired
185
+ });
186
+ // response JWT
187
+ res.status(201).json({
188
+ code: 201,
189
+ status: "AUTHORIZED",
190
+ message: "success.",
191
+ user: {
192
+ google: req.user.google,
193
+ user
194
+ },
195
+ accessToken
196
+ });
197
+ }
198
+ });
199
+ }
200
+ });
201
+ }
202
+ /**
203
+ * Facebook strategy
204
+ *
205
+ */
206
+ if (passport_config.strategy.facebook.allow) {
207
+ _app_.get(auth_endpoint + '/facebook', passport.authenticate('facebook', { scope: ['email', 'pages_show_list'] }));
208
+ // facebook callback
209
+ const facebookCallback = (passport_config.strategy.facebook.callback_endpoint) ? (passport_config.strategy.facebook.callback_endpoint[0] === "/" ? passport_config.strategy.facebook.callback_endpoint : "/" + passport_config.strategy.facebook.callback_endpoint) : "/facebook/callback";
210
+ _app_.get(auth_endpoint + facebookCallback, passport.authenticate('facebook', (err, res) => {
211
+ console.log(err, res)
212
+ }));
213
+ }
114
214
  } catch (error) {
115
215
  throw error;
116
216
  }
@@ -4,6 +4,24 @@ const md5 = require("md5");
4
4
  const secret = require("./salt").salt;
5
5
 
6
6
  module.exports = {
7
+ findOne(table, fieldCondArr, cb) {
8
+ try {
9
+ let cond = '1';
10
+ let passportFields = (passport_config.model.fields.length) ? passport_config.model.fields : ["id", "name", "email"];
11
+ // Generate condition
12
+ Object.keys(fieldCondArr).forEach(key => {
13
+ cond += ' AND ' + key + '=' + fieldCondArr[key]
14
+ });
15
+ // raw SQL
16
+ let sql = 'SELECT ?? FROM ?? WHERE ' + cond;
17
+ const pool = eval("mysql." + passport_config.model.name);
18
+ pool.query(sql, [passportFields, table], (err, row) => {
19
+ cb(err, row);
20
+ });
21
+ } catch (error) {
22
+ cb(error, null);
23
+ }
24
+ },
7
25
  store(fields, cb) {
8
26
  try {
9
27
  let keys = [];
@@ -20,16 +38,11 @@ module.exports = {
20
38
  });
21
39
  let sql = 'INSERT INTO ?? (' + keys.join() + ') VALUES (' + escaped.join() + ')';
22
40
  const pool = eval("mysql." + passport_config.model.name);
23
- pool.query(sql, values, (err) => {
24
- if (err) {
25
- cb({ code: 500, status: "CREATE_FAILED", error: err });
26
- } else {
27
- cb({ code: 201, status: "CREATE_SUCCEED", message: "created." });
28
- }
41
+ pool.query(sql, values, (err, result) => {
42
+ cb(err, result);
29
43
  });
30
-
31
44
  } catch (error) {
32
- cb({ code: 500, err: error });
45
+ cb(error, null);
33
46
  }
34
47
  },
35
48
  update(someFields, id, cb) {
@@ -49,15 +62,11 @@ module.exports = {
49
62
  values.push(id);
50
63
  let sql = 'UPDATE ?? SET ' + keys.join() + ' WHERE id = ?';
51
64
  const pool = eval("mysql." + passport_config.model.name);
52
- pool.query(sql, values, (err) => {
53
- if (err) {
54
- cb({ code: 500, status: "UPDATE_FAILED", error: err });
55
- } else {
56
- cb({ code: 200, status: "UPDATE_SUCCEED", message: "updated." });
57
- }
65
+ pool.query(sql, values, (err, result) => {
66
+ cb(err, result);
58
67
  });
59
68
  } catch (error) {
60
- cb({ code: 500, err: error });
69
+ cb(error, null);
61
70
  }
62
71
  },
63
72
  }
@@ -8,21 +8,7 @@
8
8
  "test": "node ./node_modules/jest/bin/jest __tests__ -o --watch --config"
9
9
  },
10
10
  "dependencies": {
11
- "app-root-path": "^3.0.0",
12
- "axios": "^0.18.1",
13
- "beech-api": "^3.2.11",
14
- "cookie-parser": "1.4.3",
15
- "cors": "^2.8.1",
16
- "express": "4.16.3",
17
- "express-session": "^1.17.1",
18
- "express-validator": "2.21.0",
19
- "fs": "0.0.1-security",
20
- "module-alias": "^2.2.2",
21
- "mysql": "^2.18.1",
22
- "mysql2": "^2.1.0",
23
- "nodemon": "^2.0.2",
24
- "sequelize": "^5.21.6",
25
- "walk": "^2.3.14"
11
+ "beech-api": "^3.2.11"
26
12
  },
27
13
  "devDependencies": {
28
14
  "jest": "^25.2.7",