@waline/vercel 1.2.5 → 1.3.2
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 +1 -1
- package/src/config/config.js +4 -0
- package/src/controller/comment.js +1 -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/auth/base.js +0 -31
- package/src/service/auth/github.js +0 -97
package/package.json
CHANGED
package/src/config/config.js
CHANGED
|
@@ -17,6 +17,7 @@ const {
|
|
|
17
17
|
AVATAR_PROXY,
|
|
18
18
|
GITHUB_TOKEN,
|
|
19
19
|
DETA_PROJECT_KEY,
|
|
20
|
+
OAUTH_URL,
|
|
20
21
|
|
|
21
22
|
MARKDOWN_CONFIG = '{}',
|
|
22
23
|
MARKDOWN_HIGHLIGHT,
|
|
@@ -91,6 +92,8 @@ if (AVATAR_PROXY) {
|
|
|
91
92
|
avatarProxy = !isFalse(AVATAR_PROXY) ? AVATAR_PROXY : '';
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
const oauthUrl = OAUTH_URL || 'https://user.75.team';
|
|
96
|
+
|
|
94
97
|
module.exports = {
|
|
95
98
|
workers: 1,
|
|
96
99
|
storage,
|
|
@@ -100,6 +103,7 @@ module.exports = {
|
|
|
100
103
|
secureDomains: SECURE_DOMAINS ? SECURE_DOMAINS.split(/\s*,\s*/) : undefined,
|
|
101
104
|
disableUserAgent: !isFalse(DISABLE_USERAGENT),
|
|
102
105
|
avatarProxy,
|
|
106
|
+
oauthUrl,
|
|
103
107
|
markdown,
|
|
104
108
|
mailSubject: MAIL_SUBJECT,
|
|
105
109
|
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({
|
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
|
-
};
|