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 +78 -0
- package/package.json +18 -4
- package/packages/cli/core/auth/Passport.js +130 -8
- package/packages/cli/core/configure/app.config.js +3 -0
- package/packages/cli/core/configure/passport.config.js +71 -10
- package/packages/cli/core/databases/mysql.connection.js +9 -1
- package/packages/cli/core/generator/add-on +9 -0
- package/packages/cli/core/generator/help +1 -0
- package/packages/cli/core/generator/index.js +31 -3
- package/packages/cli/core/generator/package +2 -16
- package/packages/cli/core/index.js +27 -4
- package/packages/cli/core/services/http.express.js +107 -7
- package/packages/lib/beech.js +24 -15
- package/packages/package.json +1 -15
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
|
+
"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": ">=
|
|
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
|
-
"
|
|
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"),
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
2
|
+
// Allow using jwt
|
|
3
3
|
jwt_allow: true,
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
// Custom authenticaiton endpoint, default `/authentication`
|
|
5
6
|
auth_endpoint: "",
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
// Assign your jwt secret key
|
|
7
9
|
secret: "your_jwt_secret",
|
|
8
|
-
|
|
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
|
-
//
|
|
19
|
+
// The fields for authenticate, default fields: `username` and `password`
|
|
16
20
|
username_field: "",
|
|
17
21
|
password_field: "",
|
|
18
|
-
//
|
|
22
|
+
// Show JWT fields, default show fields: ["id", "name", "email"]
|
|
19
23
|
fields: []
|
|
20
24
|
},
|
|
21
|
-
|
|
22
|
-
|
|
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[102m[90m Passed [0m [0mDatabase 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('
|
|
27
|
+
console.log(' - [36m' + val.name, '[0m->[93m', connection.config.database + ':' + connection.config.port + '[0m');
|
|
20
28
|
// checking for resolve
|
|
21
29
|
if ((index + 1) == _config_.mysql_config.length) {
|
|
22
30
|
resolve(true);
|
|
@@ -79,17 +79,25 @@ class Generator {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
} else if (this.option == 'passport') {
|
|
82
|
-
if (
|
|
83
|
-
resolve("\n[103m[90m Warning [0m[0m 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[103m[90m Warning [0m[0m 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[103m[90m Warning [0m[0m Using `add-on init` for initiate add-on.");
|
|
100
|
+
}
|
|
93
101
|
} else {
|
|
94
102
|
resolve("\n[101m Faltal [0m 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[102m[90m Passed [0m[0m The `add-on` is initialized."))
|
|
272
|
+
.catch(err => console.log(err));
|
|
273
|
+
} else {
|
|
274
|
+
resolve("\n[103m[90m Warning [0m[0m 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
|
-
"
|
|
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
|
|
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
|
|
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: [90mOFF[0m");
|
|
64
66
|
return;
|
|
65
67
|
}
|
|
68
|
+
// jwt is true
|
|
69
|
+
console.log(" - JWT: [93mON[0m");
|
|
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
|
|
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({
|
|
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({
|
|
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,
|
|
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,
|
|
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
|
}
|
package/packages/lib/beech.js
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
69
|
+
cb(error, null);
|
|
61
70
|
}
|
|
62
71
|
},
|
|
63
72
|
}
|
package/packages/package.json
CHANGED
|
@@ -8,21 +8,7 @@
|
|
|
8
8
|
"test": "node ./node_modules/jest/bin/jest __tests__ -o --watch --config"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"
|
|
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",
|