@waline/vercel 1.2.3 → 1.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waline/vercel",
3
- "version": "1.2.3",
3
+ "version": "1.3.0",
4
4
  "description": "vercel server for waline comment system",
5
5
  "repository": "https://github.com/walinejs/waline",
6
6
  "license": "MIT",
@@ -12,16 +12,16 @@
12
12
  "deta": "^1.0.1",
13
13
  "dompurify": "^2.3.3",
14
14
  "fast-csv": "^4.3.6",
15
- "jsdom": "^17.0.0",
15
+ "jsdom": "^18.0.0",
16
16
  "jsonwebtoken": "^8.5.1",
17
- "katex": "^0.13.18",
17
+ "katex": "^0.13.23",
18
18
  "leancloud-storage": "^4.12.0",
19
19
  "markdown-it": "^12.2.0",
20
20
  "markdown-it-emoji": "^2.0.0",
21
21
  "markdown-it-sub": "^1.0.0",
22
22
  "markdown-it-sup": "^1.0.0",
23
23
  "mathjax-full": "^3.2.0",
24
- "nodemailer": "^6.6.5",
24
+ "nodemailer": "^6.7.0",
25
25
  "nunjucks": "^3.2.3",
26
26
  "phpass": "^0.1.1",
27
27
  "prismjs": "^1.25.0",
@@ -35,6 +35,6 @@
35
35
  "think-mongo": "^2.1.2",
36
36
  "think-router-rest": "^1.0.5",
37
37
  "thinkjs": "^3.2.14",
38
- "ua-parser-js": "^0.7.28"
38
+ "ua-parser-js": "^0.7.31"
39
39
  }
40
40
  }
@@ -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;
@@ -70,6 +70,7 @@ module.exports = class extends BaseRest {
70
70
  'link',
71
71
  'mail',
72
72
  'nick',
73
+ 'url',
73
74
  'pid',
74
75
  'rid',
75
76
  'ua',
@@ -256,7 +257,7 @@ module.exports = class extends BaseRest {
256
257
  if (pid) {
257
258
  data.comment = data.comment.replace(
258
259
  '<p>',
259
- `<p><a class="at" href="#${pid}">@${at}</a> , `
260
+ `<p><a class="at" href="#${pid}">@${at}</a>: `
260
261
  );
261
262
  }
262
263
 
@@ -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,50 @@ 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, type, redirect } = this.get();
16
+ const { oauthUrl } = this.config();
16
17
 
17
- const userByGithub = await this.modelInstance.select({ github });
18
+ if (!code) {
19
+ const { protocol, host } = this.ctx;
20
+ const redirectUrl = `${protocol}://${host}/oauth?${qs.stringify({
21
+ redirect,
22
+ type,
23
+ })}`;
24
+ return this.redirect(
25
+ `${oauthUrl}/${type}?${qs.stringify({ redirect: redirectUrl })}`
26
+ );
27
+ }
18
28
 
19
- if (!think.isEmpty(userByGithub)) {
20
- const { redirect } = this.get();
21
- const token = jwt.sign(userByGithub[0].email, this.config('jwtKey'));
29
+ /**
30
+ * user = { id, name, email, avatar,url };
31
+ */
32
+ const params = { code };
33
+ if (type === 'facebook') {
34
+ const { protocol, host } = this.ctx;
35
+ const redirectUrl = `${protocol}://${host}/oauth?${qs.stringify({
36
+ redirect,
37
+ type,
38
+ })}`;
39
+ params.state = qs.stringify({ redirect: redirectUrl, state: '' });
40
+ }
41
+
42
+ const user = await request({
43
+ url: `${oauthUrl}/${type}?${qs.stringify(params)}`,
44
+ method: 'GET',
45
+ json: true,
46
+ headers: {
47
+ 'User-Agent': '@waline',
48
+ },
49
+ });
50
+ if (!user || !user.id) {
51
+ return this.fail();
52
+ }
53
+
54
+ const userBySocial = await this.modelInstance.select({ [type]: user.id });
55
+
56
+ if (!think.isEmpty(userBySocial)) {
57
+ const token = jwt.sign(userBySocial[0].email, this.config('jwtKey'));
22
58
 
23
59
  if (redirect) {
24
60
  return this.redirect(
@@ -29,19 +65,16 @@ module.exports = class extends think.Controller {
29
65
  return this.success();
30
66
  }
31
67
 
32
- if (!userInfo.email) {
33
- //generator a fake email if github user have no email
34
- userInfo.email = `${userInfo.github}@mail.github`;
68
+ if (!user.email) {
69
+ user.email = `${user.id}@mail.${type}`;
35
70
  }
36
71
 
37
- const { email } = userInfo;
38
72
  const current = this.ctx.state.userInfo;
39
-
40
73
  if (!think.isEmpty(current)) {
41
- const updateData = { github };
74
+ const updateData = { [type]: user.id };
42
75
 
43
- if (!current.avatar) {
44
- updateData.avatar = github.avatar;
76
+ if (!current.avatar && user.avatar) {
77
+ updateData.avatar = user.avatar;
45
78
  }
46
79
 
47
80
  await this.modelInstance.update(updateData, {
@@ -51,27 +84,30 @@ module.exports = class extends think.Controller {
51
84
  return this.success();
52
85
  }
53
86
 
54
- const userByEmail = await this.modelInstance.select({ email });
87
+ const userByEmail = await this.modelInstance.select({ email: user.email });
55
88
  if (think.isEmpty(userByEmail)) {
56
89
  const count = await this.modelInstance.count();
57
90
  const data = {
58
- ...userInfo,
91
+ display_name: user.name,
92
+ email: user.email,
93
+ url: user.url,
94
+ avatar: user.avatar,
95
+ [type]: user.id,
59
96
  password: new PasswordHash().hashPassword(Math.random()),
60
97
  type: think.isEmpty(count) ? 'administrator' : 'guest',
61
98
  };
62
99
 
63
100
  await this.modelInstance.add(data);
64
101
  } else {
65
- const updateData = { github };
102
+ const updateData = { [type]: user.id };
66
103
 
67
- if (!userByEmail.avatar) {
68
- updateData.avatar = avatar;
104
+ if (!userByEmail.avatar && user.avatar) {
105
+ updateData.avatar = user.avatar;
69
106
  }
70
- await this.modelInstance.update(updateData, { email });
107
+ await this.modelInstance.update(updateData, { email: user.email });
71
108
  }
72
109
 
73
- const { redirect } = this.get();
74
- const token = jwt.sign(email, this.config('jwtKey'));
110
+ const token = jwt.sign(user.email, this.config('jwtKey'));
75
111
 
76
112
  if (redirect) {
77
113
  return this.redirect(
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;
@@ -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
  };
@@ -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
- };