@waline/vercel 1.2.6 → 1.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/package.json +2 -1
- package/src/config/config.js +9 -1
- package/src/controller/oauth.js +69 -25
- package/src/controller/user.js +8 -4
- package/src/logic/base.js +16 -1
- package/src/logic/comment.js +26 -0
- package/src/logic/oauth.js +2 -2
- package/src/logic/token.js +1 -1
- package/src/logic/user.js +4 -4
- package/src/middleware/dashboard.js +0 -7
- package/src/service/notify.js +3 -1
- package/src/service/storage/github.js +5 -0
- package/src/service/storage/inspirecloud.js +151 -0
- package/src/service/auth/base.js +0 -31
- package/src/service/auth/github.js +0 -97
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/vercel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "vercel server for waline comment system",
|
|
5
5
|
"repository": "https://github.com/walinejs/waline",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "lizheming <i@imnerd.org>",
|
|
8
8
|
"dependencies": {
|
|
9
|
+
"@byteinspire/api": "^1.0.12",
|
|
9
10
|
"@cloudbase/node-sdk": "^2.7.1",
|
|
10
11
|
"@koa/cors": "^3.1.0",
|
|
11
12
|
"akismet": "^2.0.6",
|
package/src/config/config.js
CHANGED
|
@@ -17,6 +17,8 @@ const {
|
|
|
17
17
|
AVATAR_PROXY,
|
|
18
18
|
GITHUB_TOKEN,
|
|
19
19
|
DETA_PROJECT_KEY,
|
|
20
|
+
INSPIRECLOUD_SERVICE_SECRET,
|
|
21
|
+
OAUTH_URL,
|
|
20
22
|
|
|
21
23
|
MARKDOWN_CONFIG = '{}',
|
|
22
24
|
MARKDOWN_HIGHLIGHT,
|
|
@@ -36,7 +38,7 @@ const {
|
|
|
36
38
|
TG_TEMPLATE,
|
|
37
39
|
} = process.env;
|
|
38
40
|
|
|
39
|
-
let storage = '
|
|
41
|
+
let storage = process'inspirecloud';
|
|
40
42
|
let jwtKey = JWT_TOKEN || LEAN_KEY;
|
|
41
43
|
|
|
42
44
|
if (LEAN_KEY) {
|
|
@@ -61,6 +63,9 @@ if (LEAN_KEY) {
|
|
|
61
63
|
} else if (DETA_PROJECT_KEY) {
|
|
62
64
|
storage = 'deta';
|
|
63
65
|
jwtKey = jwtKey || DETA_PROJECT_KEY;
|
|
66
|
+
} else if (INSPIRECLOUD_SERVICE_SECRET) {
|
|
67
|
+
storage = 'inspirecloud';
|
|
68
|
+
jwtKey = jwtKey || INSPIRECLOUD_SERVICE_SECRET;
|
|
64
69
|
}
|
|
65
70
|
|
|
66
71
|
if (think.env === 'cloudbase' && storage === 'sqlite') {
|
|
@@ -91,6 +96,8 @@ if (AVATAR_PROXY) {
|
|
|
91
96
|
avatarProxy = !isFalse(AVATAR_PROXY) ? AVATAR_PROXY : '';
|
|
92
97
|
}
|
|
93
98
|
|
|
99
|
+
const oauthUrl = OAUTH_URL || 'https://user.75.team';
|
|
100
|
+
|
|
94
101
|
module.exports = {
|
|
95
102
|
workers: 1,
|
|
96
103
|
storage,
|
|
@@ -100,6 +107,7 @@ module.exports = {
|
|
|
100
107
|
secureDomains: SECURE_DOMAINS ? SECURE_DOMAINS.split(/\s*,\s*/) : undefined,
|
|
101
108
|
disableUserAgent: !isFalse(DISABLE_USERAGENT),
|
|
102
109
|
avatarProxy,
|
|
110
|
+
oauthUrl,
|
|
103
111
|
markdown,
|
|
104
112
|
mailSubject: MAIL_SUBJECT,
|
|
105
113
|
mailTemplate: MAIL_TEMPLATE,
|
package/src/controller/oauth.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
const qs = require('querystring');
|
|
1
2
|
const jwt = require('jsonwebtoken');
|
|
2
3
|
const { PasswordHash } = require('phpass');
|
|
4
|
+
const request = require('request-promise-native');
|
|
3
5
|
module.exports = class extends think.Controller {
|
|
4
6
|
constructor(ctx) {
|
|
5
7
|
super(ctx);
|
|
@@ -9,16 +11,58 @@ module.exports = class extends think.Controller {
|
|
|
9
11
|
);
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
async
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
14
|
+
async indexAction() {
|
|
15
|
+
const { code, oauth_verifier, oauth_token, type, redirect } = this.get();
|
|
16
|
+
const { oauthUrl } = this.config();
|
|
17
|
+
|
|
18
|
+
const hasCode =
|
|
19
|
+
type === 'twitter' ? oauth_token && oauth_verifier : Boolean(code);
|
|
20
|
+
if (!hasCode) {
|
|
21
|
+
const { protocol, host } = this.ctx;
|
|
22
|
+
const redirectUrl = `${protocol}://${host}/oauth?${qs.stringify({
|
|
23
|
+
redirect,
|
|
24
|
+
type,
|
|
25
|
+
})}`;
|
|
26
|
+
return this.redirect(
|
|
27
|
+
`${oauthUrl}/${type}?${qs.stringify({
|
|
28
|
+
redirect: redirectUrl,
|
|
29
|
+
state: this.ctx.state.token,
|
|
30
|
+
})}`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* user = { id, name, email, avatar,url };
|
|
36
|
+
*/
|
|
37
|
+
const params = { code, oauth_verifier, oauth_token };
|
|
38
|
+
if (type === 'facebook') {
|
|
39
|
+
const { protocol, host } = this.ctx;
|
|
40
|
+
const redirectUrl = `${protocol}://${host}/oauth?${qs.stringify({
|
|
41
|
+
redirect,
|
|
42
|
+
type,
|
|
43
|
+
})}`;
|
|
44
|
+
params.state = qs.stringify({
|
|
45
|
+
redirect: redirectUrl,
|
|
46
|
+
state: this.ctx.state.token || '',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const user = await request({
|
|
51
|
+
url: `${oauthUrl}/${type}?${qs.stringify(params)}`,
|
|
52
|
+
method: 'GET',
|
|
53
|
+
json: true,
|
|
54
|
+
headers: {
|
|
55
|
+
'User-Agent': '@waline',
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
if (!user || !user.id) {
|
|
59
|
+
return this.fail();
|
|
60
|
+
}
|
|
16
61
|
|
|
17
|
-
const
|
|
62
|
+
const userBySocial = await this.modelInstance.select({ [type]: user.id });
|
|
18
63
|
|
|
19
|
-
if (!think.isEmpty(
|
|
20
|
-
const
|
|
21
|
-
const token = jwt.sign(userByGithub[0].email, this.config('jwtKey'));
|
|
64
|
+
if (!think.isEmpty(userBySocial)) {
|
|
65
|
+
const token = jwt.sign(userBySocial[0].email, this.config('jwtKey'));
|
|
22
66
|
|
|
23
67
|
if (redirect) {
|
|
24
68
|
return this.redirect(
|
|
@@ -29,49 +73,49 @@ module.exports = class extends think.Controller {
|
|
|
29
73
|
return this.success();
|
|
30
74
|
}
|
|
31
75
|
|
|
32
|
-
if (!
|
|
33
|
-
|
|
34
|
-
userInfo.email = `${userInfo.github}@mail.github`;
|
|
76
|
+
if (!user.email) {
|
|
77
|
+
user.email = `${user.id}@mail.${type}`;
|
|
35
78
|
}
|
|
36
79
|
|
|
37
|
-
const { email } = userInfo;
|
|
38
80
|
const current = this.ctx.state.userInfo;
|
|
39
|
-
|
|
40
81
|
if (!think.isEmpty(current)) {
|
|
41
|
-
const updateData = {
|
|
82
|
+
const updateData = { [type]: user.id };
|
|
42
83
|
|
|
43
|
-
if (!current.avatar) {
|
|
44
|
-
updateData.avatar =
|
|
84
|
+
if (!current.avatar && user.avatar) {
|
|
85
|
+
updateData.avatar = user.avatar;
|
|
45
86
|
}
|
|
46
87
|
|
|
47
88
|
await this.modelInstance.update(updateData, {
|
|
48
89
|
objectId: current.objectId,
|
|
49
90
|
});
|
|
50
91
|
|
|
51
|
-
return this.
|
|
92
|
+
return this.redirect('/ui/profile');
|
|
52
93
|
}
|
|
53
94
|
|
|
54
|
-
const userByEmail = await this.modelInstance.select({ email });
|
|
95
|
+
const userByEmail = await this.modelInstance.select({ email: user.email });
|
|
55
96
|
if (think.isEmpty(userByEmail)) {
|
|
56
97
|
const count = await this.modelInstance.count();
|
|
57
98
|
const data = {
|
|
58
|
-
|
|
99
|
+
display_name: user.name,
|
|
100
|
+
email: user.email,
|
|
101
|
+
url: user.url,
|
|
102
|
+
avatar: user.avatar,
|
|
103
|
+
[type]: user.id,
|
|
59
104
|
password: new PasswordHash().hashPassword(Math.random()),
|
|
60
105
|
type: think.isEmpty(count) ? 'administrator' : 'guest',
|
|
61
106
|
};
|
|
62
107
|
|
|
63
108
|
await this.modelInstance.add(data);
|
|
64
109
|
} else {
|
|
65
|
-
const updateData = {
|
|
110
|
+
const updateData = { [type]: user.id };
|
|
66
111
|
|
|
67
|
-
if (!userByEmail.avatar) {
|
|
68
|
-
updateData.avatar = avatar;
|
|
112
|
+
if (!userByEmail.avatar && user.avatar) {
|
|
113
|
+
updateData.avatar = user.avatar;
|
|
69
114
|
}
|
|
70
|
-
await this.modelInstance.update(updateData, { email });
|
|
115
|
+
await this.modelInstance.update(updateData, { email: user.email });
|
|
71
116
|
}
|
|
72
117
|
|
|
73
|
-
const
|
|
74
|
-
const token = jwt.sign(email, this.config('jwtKey'));
|
|
118
|
+
const token = jwt.sign(user.email, this.config('jwtKey'));
|
|
75
119
|
|
|
76
120
|
if (redirect) {
|
|
77
121
|
return this.redirect(
|
package/src/controller/user.js
CHANGED
|
@@ -78,7 +78,7 @@ module.exports = class extends BaseRest {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
async putAction() {
|
|
81
|
-
const { display_name, url, password
|
|
81
|
+
const { display_name, url, password } = this.post();
|
|
82
82
|
const { objectId } = this.ctx.state.userInfo;
|
|
83
83
|
|
|
84
84
|
const updateData = {};
|
|
@@ -95,9 +95,13 @@ module.exports = class extends BaseRest {
|
|
|
95
95
|
updateData.password = new PasswordHash().hashPassword(password);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
const socials = ['github', 'twitter', 'facebook', 'google', 'weibo', 'qq'];
|
|
99
|
+
socials.forEach((social) => {
|
|
100
|
+
const nextSocial = this.post(social);
|
|
101
|
+
if (think.isString(nextSocial)) {
|
|
102
|
+
updateData[social] = nextSocial;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
101
105
|
|
|
102
106
|
if (think.isEmpty(updateData)) {
|
|
103
107
|
return this.success();
|
package/src/logic/base.js
CHANGED
|
@@ -43,7 +43,21 @@ module.exports = class extends think.Logic {
|
|
|
43
43
|
|
|
44
44
|
const user = await this.modelInstance.select(
|
|
45
45
|
{ email: userMail },
|
|
46
|
-
{
|
|
46
|
+
{
|
|
47
|
+
field: [
|
|
48
|
+
'email',
|
|
49
|
+
'url',
|
|
50
|
+
'display_name',
|
|
51
|
+
'type',
|
|
52
|
+
'github',
|
|
53
|
+
'twitter',
|
|
54
|
+
'facebook',
|
|
55
|
+
'google',
|
|
56
|
+
'weibo',
|
|
57
|
+
'qq',
|
|
58
|
+
'avatar',
|
|
59
|
+
],
|
|
60
|
+
}
|
|
47
61
|
);
|
|
48
62
|
if (think.isEmpty(user)) {
|
|
49
63
|
return;
|
|
@@ -65,5 +79,6 @@ module.exports = class extends think.Logic {
|
|
|
65
79
|
userInfo.avatar = avatarUrl;
|
|
66
80
|
userInfo.mailMd5 = helper.md5(userInfo.email);
|
|
67
81
|
this.ctx.state.userInfo = userInfo;
|
|
82
|
+
this.ctx.state.token = token;
|
|
68
83
|
}
|
|
69
84
|
};
|
package/src/logic/comment.js
CHANGED
|
@@ -205,4 +205,30 @@ module.exports = class extends Base {
|
|
|
205
205
|
return this.ctx.throw(401);
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @api {POST} /comment/:id update comment data
|
|
211
|
+
* @apiGroup Comment
|
|
212
|
+
* @apiVersion 0.0.1
|
|
213
|
+
*
|
|
214
|
+
* @apiParam {String} [nick] post comment user nick name
|
|
215
|
+
* @apiParam {String} [mail] post comment user mail address
|
|
216
|
+
* @apiParam {String} [link] post comment user link
|
|
217
|
+
* @apiParam {String} [comment] post comment text
|
|
218
|
+
* @apiParam {String} [url] the artcile url path of comment
|
|
219
|
+
*
|
|
220
|
+
* @apiSuccess (200) {Number} errno 0
|
|
221
|
+
* @apiSuccess (200) {String} errmsg return error message if error
|
|
222
|
+
*/
|
|
223
|
+
putAction() {}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* @api {DELETE} /comment/:id delete comment
|
|
227
|
+
* @apiGroup Comment
|
|
228
|
+
* @apiVersion 0.0.1
|
|
229
|
+
*
|
|
230
|
+
* @apiSuccess (200) {Number} errno 0
|
|
231
|
+
* @apiSuccess (200) {String} errmsg return error message if error
|
|
232
|
+
*/
|
|
233
|
+
deleteAction() {}
|
|
208
234
|
};
|
package/src/logic/oauth.js
CHANGED
package/src/logic/token.js
CHANGED
|
@@ -12,7 +12,7 @@ module.exports = class extends Base {
|
|
|
12
12
|
* @apiSuccess (200) {String} data.avatar user avatar
|
|
13
13
|
* @apiSuccess (200) {String} data.createdAt user register time
|
|
14
14
|
* @apiSuccess (200) {String} data.display_name user nick name
|
|
15
|
-
* @apiSuccess (200) {String} data.
|
|
15
|
+
* @apiSuccess (200) {String} data.email user email address
|
|
16
16
|
* @apiSuccess (200) {String} data.github user github account name
|
|
17
17
|
* @apiSuccess (200) {String} data.mailMd5 user mail md5
|
|
18
18
|
* @apiSuccess (200) {String} data.objectId user id
|
package/src/logic/user.js
CHANGED
|
@@ -21,10 +21,10 @@ module.exports = class extends Base {
|
|
|
21
21
|
* @apiGroup User
|
|
22
22
|
* @apiVersion 0.0.1
|
|
23
23
|
*
|
|
24
|
-
* @apiParam {String} display_name user new nick name
|
|
25
|
-
* @apiParam {String} url user new link
|
|
26
|
-
* @apiParam {String} password user new password
|
|
27
|
-
* @apiParam {String} github user github account name
|
|
24
|
+
* @apiParam {String} [display_name] user new nick name
|
|
25
|
+
* @apiParam {String} [url] user new link
|
|
26
|
+
* @apiParam {String} [password] user new password
|
|
27
|
+
* @apiParam {String} [github] user github account name
|
|
28
28
|
*
|
|
29
29
|
* @apiSuccess (200) {Number} errno 0
|
|
30
30
|
* @apiSuccess (200) {String} errmsg return error message if error
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
const { GITHUB_ID, GITHUB_SECRET } = process.env;
|
|
2
|
-
|
|
3
1
|
module.exports = function () {
|
|
4
|
-
const socials = [['github', GITHUB_ID && GITHUB_SECRET]]
|
|
5
|
-
.filter(([, condition]) => condition)
|
|
6
|
-
.map(([name]) => name);
|
|
7
|
-
|
|
8
2
|
return (ctx) => {
|
|
9
3
|
ctx.type = 'html';
|
|
10
4
|
ctx.body = `<!doctype html>
|
|
@@ -18,7 +12,6 @@ module.exports = function () {
|
|
|
18
12
|
<script>
|
|
19
13
|
window.SITE_URL = ${JSON.stringify(process.env.SITE_URL)};
|
|
20
14
|
window.SITE_NAME = ${JSON.stringify(process.env.SITE_NAME)};
|
|
21
|
-
window.ALLOW_SOCIALS = ${JSON.stringify(socials)};
|
|
22
15
|
</script>
|
|
23
16
|
<script src="https://cdn.jsdelivr.net/npm/@waline/admin"></script>
|
|
24
17
|
</body>
|
package/src/service/notify.js
CHANGED
|
@@ -242,7 +242,9 @@ module.exports = class extends think.Service {
|
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
const disallowList = ['github'].map(
|
|
245
|
+
const disallowList = ['github', 'twitter', 'facebook'].map(
|
|
246
|
+
(social) => 'mail.' + social
|
|
247
|
+
);
|
|
246
248
|
const fakeMail = new RegExp(`@(${disallowList.join('|')})$`, 'i');
|
|
247
249
|
if (parent && !fakeMail.test(parent.mail) && comment.status !== 'waiting') {
|
|
248
250
|
mailList.push({
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const inspirecloud = require('@byteinspire/api');
|
|
2
|
+
const Base = require('./base');
|
|
3
|
+
|
|
4
|
+
module.exports = class extends Base {
|
|
5
|
+
constructor(tableName) {
|
|
6
|
+
super(tableName);
|
|
7
|
+
this.db = inspirecloud.db;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
where(where) {
|
|
11
|
+
if (think.isEmpty(where)) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const _where = {};
|
|
16
|
+
const parseKey = (k) => (k === 'objectId' ? '_id' : k);
|
|
17
|
+
for (const k in where) {
|
|
18
|
+
if (think.isString(where[k])) {
|
|
19
|
+
_where[parseKey(k)] = k === 'objectId' ? this.db.ObjectId(where[k]) : where[k];
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (where[k] === undefined) {
|
|
23
|
+
_where[parseKey(k)] = undefined;
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(where[k])) {
|
|
26
|
+
if (where[k][0]) {
|
|
27
|
+
const handler = where[k][0].toUpperCase();
|
|
28
|
+
switch (handler) {
|
|
29
|
+
case 'IN':
|
|
30
|
+
_where[parseKey(k)] = this.db.in(
|
|
31
|
+
k === 'objectId'
|
|
32
|
+
? where[k][1].map(this.db.ObjectId)
|
|
33
|
+
: where[k][1]
|
|
34
|
+
);
|
|
35
|
+
break;
|
|
36
|
+
case 'NOT IN':
|
|
37
|
+
_where[parseKey(k)] = this.db.nin(
|
|
38
|
+
k === 'objectId'
|
|
39
|
+
? where[k][1].map(this.db.ObjectId)
|
|
40
|
+
: where[k][1]
|
|
41
|
+
);
|
|
42
|
+
break;
|
|
43
|
+
case 'LIKE': {
|
|
44
|
+
const first = where[k][1][0];
|
|
45
|
+
const last = where[k][1].slice(-1);
|
|
46
|
+
let reg;
|
|
47
|
+
if (first === '%' && last === '%') {
|
|
48
|
+
reg = new RegExp(where[k][1].slice(1, -1));
|
|
49
|
+
} else if (first === '%') {
|
|
50
|
+
reg = new RegExp(where[k][1].slice(1) + '$');
|
|
51
|
+
} else if (last === '%') {
|
|
52
|
+
reg = new RegExp('^' + where[k][1].slice(0, -1));
|
|
53
|
+
}
|
|
54
|
+
_where[parseKey(k)] = this.db.regex(reg);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case '!=':
|
|
58
|
+
_where[parseKey(k)] = this.db.ne(where[k]);
|
|
59
|
+
break;
|
|
60
|
+
case '>':
|
|
61
|
+
_where[parseKey(k)] = this.db.gt(where[k]);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return _where;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async _select(where, { desc, limit, offset, field } = {}) {
|
|
72
|
+
const instance = this.db.table(this.tableName);
|
|
73
|
+
const query = instance.where(this.where(where));
|
|
74
|
+
|
|
75
|
+
if (desc) {
|
|
76
|
+
query.sort({ [desc]: -1 });
|
|
77
|
+
}
|
|
78
|
+
if (limit) {
|
|
79
|
+
query.limit(limit);
|
|
80
|
+
}
|
|
81
|
+
if (offset) {
|
|
82
|
+
query.skip(offset);
|
|
83
|
+
}
|
|
84
|
+
if (field) {
|
|
85
|
+
const _field = {};
|
|
86
|
+
field.forEach((f) => {
|
|
87
|
+
_field[f] = 1;
|
|
88
|
+
});
|
|
89
|
+
query.projection(_field);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const data = await query.find();
|
|
93
|
+
data.forEach((item) => {
|
|
94
|
+
item.objectId = item._id.toString();
|
|
95
|
+
delete item._id;
|
|
96
|
+
});
|
|
97
|
+
return data;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async select(where, options = {}) {
|
|
101
|
+
let data = [];
|
|
102
|
+
let ret = [];
|
|
103
|
+
do {
|
|
104
|
+
options.offset = (options.offset || 0) + data.length;
|
|
105
|
+
ret = await this._select(where, options);
|
|
106
|
+
data = data.concat(ret);
|
|
107
|
+
} while (ret.length === 1000);
|
|
108
|
+
|
|
109
|
+
return data;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async count(where = {}) {
|
|
113
|
+
const instance = this.db.table(this.tableName);
|
|
114
|
+
const query = instance.where(this.where(where));
|
|
115
|
+
|
|
116
|
+
return query.count();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async add(data) {
|
|
120
|
+
const instance = this.db.table(this.tableName);
|
|
121
|
+
const tableData = instance.create(data);
|
|
122
|
+
await instance.save(tableData);
|
|
123
|
+
|
|
124
|
+
tableData.objectId = tableData._id.toString();
|
|
125
|
+
delete tableData._id;
|
|
126
|
+
return tableData;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async update(data, where) {
|
|
130
|
+
const instance = this.db.table(this.tableName);
|
|
131
|
+
const query = instance.where(this.where(where));
|
|
132
|
+
const items = await query.find();
|
|
133
|
+
|
|
134
|
+
return Promise.all(
|
|
135
|
+
items.map(async (item) => {
|
|
136
|
+
const updateData = typeof data === 'function' ? data(item) : data;
|
|
137
|
+
for (const k in updateData) {
|
|
138
|
+
item[k] = updateData[k];
|
|
139
|
+
}
|
|
140
|
+
await instance.save(item);
|
|
141
|
+
return item;
|
|
142
|
+
})
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async delete(where) {
|
|
147
|
+
const instance = this.db.table(this.tableName);
|
|
148
|
+
const query = instance.where(this.where(where));
|
|
149
|
+
return query.delete();
|
|
150
|
+
}
|
|
151
|
+
};
|
package/src/service/auth/base.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const { parse } = require('url');
|
|
2
|
-
|
|
3
|
-
module.exports = class extends think.Service {
|
|
4
|
-
constructor(app) {
|
|
5
|
-
super(app);
|
|
6
|
-
this.app = app;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
getCompleteUrl(url = '') {
|
|
10
|
-
if (url.slice(0, 4) === 'http') {
|
|
11
|
-
try {
|
|
12
|
-
const { host } = parse(url);
|
|
13
|
-
|
|
14
|
-
if (this.app.host.toLowerCase() !== host.toLowerCase()) {
|
|
15
|
-
throw new Error(403);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return url;
|
|
19
|
-
} catch (err) {
|
|
20
|
-
// ignore error
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
const protocol = this.app.header('x-forwarded-proto') || 'http';
|
|
24
|
-
|
|
25
|
-
if (!/^\//.test(url)) {
|
|
26
|
-
url = `/${url}`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return `${protocol}://${this.app.ctx.host}${url}`;
|
|
30
|
-
}
|
|
31
|
-
};
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
const qs = require('querystring');
|
|
2
|
-
const request = require('request-promise-native');
|
|
3
|
-
const Base = require('./base');
|
|
4
|
-
|
|
5
|
-
const OAUTH_URL = 'https://github.com/login/oauth/authorize';
|
|
6
|
-
const ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token';
|
|
7
|
-
const USER_INFO_URL = 'https://api.github.com/user';
|
|
8
|
-
const USER_EMAILS = 'https://api.github.com/user/emails';
|
|
9
|
-
const { GITHUB_ID, GITHUB_SECRET } = process.env;
|
|
10
|
-
module.exports = class extends Base {
|
|
11
|
-
getAuthUrl(opts) {
|
|
12
|
-
const params = {
|
|
13
|
-
client_id: GITHUB_ID,
|
|
14
|
-
redirect_uri: opts.rdUrl,
|
|
15
|
-
scope: 'read:user,user:email',
|
|
16
|
-
};
|
|
17
|
-
return OAUTH_URL + '?' + qs.stringify(params);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async getAccessToken(opts) {
|
|
21
|
-
const params = {
|
|
22
|
-
client_id: GITHUB_ID,
|
|
23
|
-
client_secret: GITHUB_SECRET,
|
|
24
|
-
code: opts.code,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const body = await request.post({
|
|
28
|
-
url: ACCESS_TOKEN_URL,
|
|
29
|
-
headers: { Accept: 'application/json' },
|
|
30
|
-
form: params,
|
|
31
|
-
json: true,
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return body;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async getUserInfoByToken(opts) {
|
|
38
|
-
const userInfo = await request.get({
|
|
39
|
-
url: USER_INFO_URL,
|
|
40
|
-
headers: {
|
|
41
|
-
'User-Agent': '@waline',
|
|
42
|
-
Authorization: 'token ' + opts.access_token,
|
|
43
|
-
},
|
|
44
|
-
json: true,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
if (!userInfo.email) {
|
|
48
|
-
const emails = await request.get({
|
|
49
|
-
url: USER_EMAILS,
|
|
50
|
-
headers: {
|
|
51
|
-
'User-Agent': '@waline',
|
|
52
|
-
Authorization: 'token ' + opts.access_token,
|
|
53
|
-
},
|
|
54
|
-
json: true,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
if (emails.length) {
|
|
58
|
-
userInfo.email = emails[0].email;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
github: userInfo.login,
|
|
64
|
-
display_name: userInfo.name,
|
|
65
|
-
email: userInfo.email,
|
|
66
|
-
url: userInfo.blog,
|
|
67
|
-
avatar: userInfo.avatar_url,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async redirect() {
|
|
72
|
-
const { app } = this;
|
|
73
|
-
const { redirect, state } = app.get();
|
|
74
|
-
const rdUrlAfterLogin = this.getCompleteUrl(redirect);
|
|
75
|
-
|
|
76
|
-
const params = { redirect: rdUrlAfterLogin, state };
|
|
77
|
-
const signinUrl =
|
|
78
|
-
this.getCompleteUrl('/oauth/github') + '?' + qs.stringify(params);
|
|
79
|
-
const AUTH_URL = this.getAuthUrl({ rdUrl: signinUrl });
|
|
80
|
-
|
|
81
|
-
app.redirect(AUTH_URL);
|
|
82
|
-
|
|
83
|
-
return app.success();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async getUserInfo() {
|
|
87
|
-
const { code } = this.app.get();
|
|
88
|
-
|
|
89
|
-
if (!code) {
|
|
90
|
-
return this.redirect();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const accessTokenInfo = await this.getAccessToken({ code });
|
|
94
|
-
|
|
95
|
-
return this.getUserInfoByToken(accessTokenInfo);
|
|
96
|
-
}
|
|
97
|
-
};
|