@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waline/vercel",
3
- "version": "1.2.5",
3
+ "version": "1.3.2",
4
4
  "description": "vercel server for waline comment system",
5
5
  "repository": "https://github.com/walinejs/waline",
6
6
  "license": "MIT",
@@ -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,
@@ -19,7 +19,7 @@ async function formatCmt(
19
19
  }
20
20
 
21
21
  const user = users.find(({ objectId }) => user_id === objectId);
22
- if (user) {
22
+ if (!think.isEmpty(user)) {
23
23
  comment.nick = user.display_name;
24
24
  comment.mail = user.email;
25
25
  comment.link = user.url;
@@ -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 githubAction() {
13
- const instance = this.service('auth/github', this);
14
- const userInfo = await instance.getUserInfo();
15
- const { github, avatar } = userInfo;
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 userByGithub = await this.modelInstance.select({ github });
62
+ const userBySocial = await this.modelInstance.select({ [type]: user.id });
18
63
 
19
- if (!think.isEmpty(userByGithub)) {
20
- const { redirect } = this.get();
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 (!userInfo.email) {
33
- //generator a fake email if github user have no email
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 = { github };
82
+ const updateData = { [type]: user.id };
42
83
 
43
- if (!current.avatar) {
44
- updateData.avatar = github.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.success();
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
- ...userInfo,
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 = { github };
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 { redirect } = this.get();
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(
@@ -78,7 +78,7 @@ module.exports = class extends BaseRest {
78
78
  }
79
79
 
80
80
  async putAction() {
81
- const { display_name, url, password, github } = this.post();
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
- if (think.isString(github)) {
99
- updateData.github = github;
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
- { field: ['email', 'url', 'display_name', 'type', 'github', 'avatar'] }
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
  };
@@ -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
  };
@@ -2,9 +2,9 @@ const Base = require('./base');
2
2
 
3
3
  module.exports = class extends Base {
4
4
  /**
5
- * @api {GET} /oauth/github github oauth api
5
+ * @api {GET} /oauth oauth api
6
6
  * @apiGroup OAuth
7
7
  * @apiVersion 0.0.1
8
8
  */
9
- githubAction() {}
9
+ indexAction() {}
10
10
  };
@@ -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.emal user email address
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>
@@ -242,7 +242,9 @@ module.exports = class extends think.Service {
242
242
  }
243
243
  }
244
244
 
245
- const disallowList = ['github'].map((social) => 'mail.' + social);
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({
@@ -31,6 +31,11 @@ const CSV_HEADERS = {
31
31
  'url',
32
32
  'avatar',
33
33
  'github',
34
+ 'twitter',
35
+ 'facebook',
36
+ 'google',
37
+ 'weibo',
38
+ 'qq',
34
39
  'createdAt',
35
40
  'updatedAt',
36
41
  ],
@@ -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
- };