@waline/vercel 1.20.1 → 1.22.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.20.1",
3
+ "version": "1.22.0",
4
4
  "description": "vercel server for waline comment system",
5
5
  "keywords": [
6
6
  "waline",
@@ -6,7 +6,7 @@ const { getMarkdownParser } = require('../service/markdown');
6
6
  const markdownParser = getMarkdownParser();
7
7
 
8
8
  async function formatCmt(
9
- { ua, user_id, ip, ...comment },
9
+ { ua, ip, ...comment },
10
10
  users = [],
11
11
  { avatarProxy },
12
12
  loginUser
@@ -20,7 +20,7 @@ async function formatCmt(
20
20
  comment.os = [ua.os.name, ua.os.version].filter((v) => v).join(' ');
21
21
  }
22
22
 
23
- const user = users.find(({ objectId }) => user_id === objectId);
23
+ const user = users.find(({ objectId }) => comment.user_id === objectId);
24
24
 
25
25
  if (!think.isEmpty(user)) {
26
26
  comment.nick = user.display_name;
@@ -42,10 +42,12 @@ async function formatCmt(
42
42
 
43
43
  const isAdmin = loginUser && loginUser.type === 'administrator';
44
44
 
45
+ if (loginUser) {
46
+ comment.orig = comment.comment;
47
+ }
45
48
  if (!isAdmin) {
46
49
  delete comment.mail;
47
50
  } else {
48
- comment.orig = comment.comment;
49
51
  comment.ip = ip;
50
52
  }
51
53
 
@@ -592,7 +594,7 @@ module.exports = class extends BaseRest {
592
594
  if (parentComment.user_id) {
593
595
  parentUser = await this.service(
594
596
  `storage/${this.config('storage')}`,
595
- 'User'
597
+ 'Users'
596
598
  ).select({
597
599
  objectId: parentComment.user_id,
598
600
  });
@@ -642,26 +644,21 @@ module.exports = class extends BaseRest {
642
644
 
643
645
  async putAction() {
644
646
  const { userInfo } = this.ctx.state;
645
- let data = this.post();
647
+ const isAdmin = userInfo.type === 'administrator';
648
+ let data = isAdmin ? this.post() : this.post('comment,like');
646
649
  let oldData = await this.modelInstance.select({ objectId: this.id });
647
650
 
648
- if (think.isEmpty(oldData)) {
651
+ if (think.isEmpty(oldData) || think.isEmpty(data)) {
649
652
  return this.success();
650
653
  }
651
654
 
652
655
  oldData = oldData[0];
653
- if (think.isEmpty(userInfo) || userInfo.type !== 'administrator') {
654
- if (!think.isBoolean(data.like)) {
655
- return this.success();
656
- }
657
-
656
+ if (think.isBoolean(data.like)) {
658
657
  const likeIncMax = this.config('LIKE_INC_MAX') || 1;
659
658
 
660
- data = {
661
- like:
662
- (Number(oldData.like) || 0) +
663
- (data.like ? Math.ceil(Math.random() * likeIncMax) : -1),
664
- };
659
+ data.like =
660
+ (Number(oldData.like) || 0) +
661
+ (data.like ? Math.ceil(Math.random() * likeIncMax) : -1);
665
662
  }
666
663
 
667
664
  const preUpdateResp = await this.hook('preUpdate', {
@@ -677,23 +674,29 @@ module.exports = class extends BaseRest {
677
674
  objectId: this.id,
678
675
  });
679
676
 
677
+ let cmtUser;
678
+
679
+ if (!think.isEmpty(newData) && newData[0].user_id) {
680
+ cmtUser = await this.service(
681
+ `storage/${this.config('storage')}`,
682
+ 'Users'
683
+ ).select({
684
+ objectId: newData[0].user_id,
685
+ });
686
+ cmtUser = cmtUser[0];
687
+ }
688
+ const cmtReturn = await formatCmt(
689
+ newData[0],
690
+ cmtUser ? [cmtUser] : [],
691
+ this.config(),
692
+ userInfo
693
+ );
694
+
680
695
  if (
681
696
  oldData.status === 'waiting' &&
682
697
  data.status === 'approved' &&
683
698
  oldData.pid
684
699
  ) {
685
- let cmtUser;
686
-
687
- if (newData.user_id) {
688
- cmtUser = await this.service(
689
- `storage/${this.config('storage')}`,
690
- 'User'
691
- ).select({
692
- objectId: newData.user_id,
693
- });
694
- cmtUser = cmtUser[0];
695
- }
696
-
697
700
  let pComment = await this.modelInstance.select({
698
701
  objectId: oldData.pid,
699
702
  });
@@ -705,7 +708,7 @@ module.exports = class extends BaseRest {
705
708
  if (pComment.user_id) {
706
709
  pUser = await this.service(
707
710
  `storage/${this.config('storage')}`,
708
- 'User'
711
+ 'Users'
709
712
  ).select({
710
713
  objectId: pComment.user_id,
711
714
  });
@@ -713,12 +716,6 @@ module.exports = class extends BaseRest {
713
716
  }
714
717
 
715
718
  const notify = this.service('notify');
716
- const cmtReturn = await formatCmt(
717
- newData,
718
- cmtUser ? [cmtUser] : [],
719
- this.config(),
720
- userInfo
721
- );
722
719
  const pcmtReturn = await formatCmt(
723
720
  pComment,
724
721
  pUser ? [pUser] : [],
@@ -727,7 +724,7 @@ module.exports = class extends BaseRest {
727
724
  );
728
725
 
729
726
  await notify.run(
730
- { ...cmtReturn, mail: newData.mail },
727
+ { ...cmtReturn, mail: newData[0].mail },
731
728
  { ...pcmtReturn, mail: pComment.mail },
732
729
  true
733
730
  );
@@ -735,7 +732,7 @@ module.exports = class extends BaseRest {
735
732
 
736
733
  await this.hook('postUpdate', data);
737
734
 
738
- return this.success();
735
+ return this.success(cmtReturn);
739
736
  }
740
737
 
741
738
  async deleteAction() {
@@ -1,7 +1,6 @@
1
1
  const jwt = require('jsonwebtoken');
2
2
  const fetch = require('node-fetch');
3
3
  const { PasswordHash } = require('phpass');
4
- const qs = require('querystring');
5
4
 
6
5
  module.exports = class extends think.Controller {
7
6
  constructor(ctx) {
@@ -21,16 +20,16 @@ module.exports = class extends think.Controller {
21
20
 
22
21
  if (!hasCode) {
23
22
  const { serverURL } = this.ctx;
24
- const redirectUrl = `${serverURL}/oauth?${qs.stringify({
23
+ const redirectUrl = `${serverURL}/oauth?${new URLSearchParams({
25
24
  redirect,
26
25
  type,
27
- })}`;
26
+ }).toString()}`;
28
27
 
29
28
  return this.redirect(
30
- `${oauthUrl}/${type}?${qs.stringify({
29
+ `${oauthUrl}/${type}?${new URLSearchParams({
31
30
  redirect: redirectUrl,
32
31
  state: this.ctx.state.token,
33
- })}`
32
+ }).toString()}`
34
33
  );
35
34
  }
36
35
 
@@ -41,23 +40,26 @@ module.exports = class extends think.Controller {
41
40
 
42
41
  if (type === 'facebook') {
43
42
  const { serverURL } = this.ctx;
44
- const redirectUrl = `${serverURL}/oauth?${qs.stringify({
43
+ const redirectUrl = `${serverURL}/oauth?${new URLSearchParams({
45
44
  redirect,
46
45
  type,
47
- })}`;
46
+ }).toString()}`;
48
47
 
49
- params.state = qs.stringify({
48
+ params.state = new URLSearchParams({
50
49
  redirect: redirectUrl,
51
50
  state: this.ctx.state.token || '',
52
51
  });
53
52
  }
54
53
 
55
- const user = await fetch(`${oauthUrl}/${type}?${qs.stringify(params)}`, {
56
- method: 'GET',
57
- headers: {
58
- 'user-agent': '@waline',
59
- },
60
- }).then((resp) => resp.json());
54
+ const user = await fetch(
55
+ `${oauthUrl}/${type}?${new URLSearchParams(params).toString()}`,
56
+ {
57
+ method: 'GET',
58
+ headers: {
59
+ 'user-agent': '@waline',
60
+ },
61
+ }
62
+ ).then((resp) => resp.json());
61
63
 
62
64
  if (!user || !user.id) {
63
65
  return this.fail(user);
@@ -1,4 +1,3 @@
1
- const qs = require('querystring');
2
1
  const { PasswordHash } = require('phpass');
3
2
  const BaseRest = require('./rest');
4
3
 
@@ -82,7 +81,7 @@ module.exports = class extends BaseRest {
82
81
  const apiUrl =
83
82
  this.ctx.serverURL +
84
83
  '/verification?' +
85
- qs.stringify({ token, email: data.email });
84
+ new URLSearchParams({ token, email: data.email }).toString();
86
85
 
87
86
  await notify.transporter.sendMail({
88
87
  from:
package/src/logic/base.js CHANGED
@@ -1,4 +1,6 @@
1
1
  const path = require('path');
2
+ const qs = require('querystring');
3
+ const fetch = require('node-fetch');
2
4
  const jwt = require('jsonwebtoken');
3
5
  const helper = require('think-helper');
4
6
 
@@ -122,4 +124,35 @@ module.exports = class extends think.Logic {
122
124
 
123
125
  return '';
124
126
  }
127
+
128
+ async useCaptchaCheck() {
129
+ const { RECAPTCHA_V3_SECRET } = process.env;
130
+
131
+ if (!RECAPTCHA_V3_SECRET) {
132
+ return;
133
+ }
134
+ const { recaptchaV3 } = this.post();
135
+
136
+ if (!recaptchaV3) {
137
+ return this.ctx.throw(403);
138
+ }
139
+
140
+ const query = qs.stringify({
141
+ secret: RECAPTCHA_V3_SECRET,
142
+ response: recaptchaV3,
143
+ remoteip: this.ctx.ip,
144
+ });
145
+ const recaptchaV3Result = await fetch(
146
+ `https://recaptcha.net/recaptcha/api/siteverify?${query}`
147
+ ).then((resp) => resp.json());
148
+
149
+ if (!recaptchaV3Result.success) {
150
+ think.logger.debug(
151
+ 'RecaptchaV3 Result:',
152
+ JSON.stringify(recaptchaV3Result, null, '\t')
153
+ );
154
+
155
+ return this.ctx.throw(403);
156
+ }
157
+ }
125
158
  };
@@ -1,18 +1,7 @@
1
1
  const Base = require('./base');
2
2
 
3
3
  module.exports = class extends Base {
4
- async __before() {
5
- await super.__before();
6
-
7
- const { type, path } = this.get();
8
- const { like } = this.post();
9
- const isAllowedGet = this.isGet && (type !== 'list' || path);
10
- const isAllowedPut = this.ctx.isMethod('PUT') && think.isBoolean(like);
11
-
12
- if (this.isPost || isAllowedGet || isAllowedPut) {
13
- return;
14
- }
15
-
4
+ checkAdmin() {
16
5
  const { userInfo } = this.ctx.state;
17
6
 
18
7
  if (think.isEmpty(userInfo)) {
@@ -116,7 +105,12 @@ module.exports = class extends Base {
116
105
  * @apiSuccess (200) {String} response.type comment login user type
117
106
  */
118
107
  getAction() {
119
- const { type } = this.get();
108
+ const { type, path } = this.get();
109
+ const isAllowedGet = type !== 'list' || path;
110
+
111
+ if (!isAllowedGet) {
112
+ this.checkAdmin();
113
+ }
120
114
 
121
115
  switch (type) {
122
116
  case 'recent':
@@ -206,13 +200,19 @@ module.exports = class extends Base {
206
200
  * @apiSuccess (200) {String} data.avatar comment user avatar
207
201
  * @apiSuccess (200) {String} data.type comment login user type
208
202
  */
209
- postAction() {
203
+ async postAction() {
210
204
  const { LOGIN } = process.env;
211
205
  const { userInfo } = this.ctx.state;
212
206
 
213
- if (LOGIN === 'force' && think.isEmpty(userInfo)) {
207
+ if (!think.isEmpty(userInfo)) {
208
+ return;
209
+ }
210
+
211
+ if (LOGIN === 'force') {
214
212
  return this.ctx.throw(401);
215
213
  }
214
+
215
+ return this.useCaptchaCheck();
216
216
  }
217
217
 
218
218
  /**
@@ -230,17 +230,46 @@ module.exports = class extends Base {
230
230
  * @apiSuccess (200) {Number} errno 0
231
231
  * @apiSuccess (200) {String} errmsg return error message if error
232
232
  */
233
- putAction() {
233
+ async putAction() {
234
234
  const { userInfo } = this.ctx.state;
235
+ const { like } = this.post();
235
236
 
236
- if (think.isEmpty(userInfo) || userInfo.type !== 'administrator') {
237
+ // 1. like
238
+ if (think.isEmpty(userInfo) && think.isBoolean(like)) {
237
239
  this.rules = {
238
240
  like: {
239
241
  required: true,
240
242
  boolean: true,
241
243
  },
242
244
  };
245
+
246
+ return;
243
247
  }
248
+
249
+ if (think.isEmpty(userInfo)) {
250
+ return this.ctx.throw(401);
251
+ }
252
+
253
+ // 2. administrator
254
+ if (userInfo.type === 'administrator') {
255
+ return;
256
+ }
257
+
258
+ // 3. comment author modify comment content
259
+ const modelInstance = this.service(
260
+ `storage/${this.config('storage')}`,
261
+ 'Comment'
262
+ );
263
+ const commentData = await modelInstance.select({
264
+ user_id: userInfo.objectId,
265
+ objectId: this.id,
266
+ });
267
+
268
+ if (!think.isEmpty(commentData)) {
269
+ return;
270
+ }
271
+
272
+ return this.ctx.throw(403);
244
273
  }
245
274
 
246
275
  /**
@@ -251,5 +280,30 @@ module.exports = class extends Base {
251
280
  * @apiSuccess (200) {Number} errno 0
252
281
  * @apiSuccess (200) {String} errmsg return error message if error
253
282
  */
254
- deleteAction() {}
283
+ async deleteAction() {
284
+ const { userInfo } = this.ctx.state;
285
+
286
+ if (think.isEmpty(userInfo)) {
287
+ return this.ctx.throw(401);
288
+ }
289
+
290
+ if (userInfo.type === 'administrator') {
291
+ return;
292
+ }
293
+
294
+ const modelInstance = this.service(
295
+ `storage/${this.config('storage')}`,
296
+ 'Comment'
297
+ );
298
+ const commentData = await modelInstance.select({
299
+ user_id: userInfo.objectId,
300
+ objectId: this.id,
301
+ });
302
+
303
+ if (!think.isEmpty(commentData)) {
304
+ return;
305
+ }
306
+
307
+ return this.ctx.throw(403);
308
+ }
255
309
  };
@@ -32,7 +32,9 @@ module.exports = class extends Base {
32
32
  * @apiSuccess (200) {Number} errno 0
33
33
  * @apiSuccess (200) {String} errmsg return error message if error
34
34
  */
35
- postAction() {}
35
+ postAction() {
36
+ return this.useCaptchaCheck();
37
+ }
36
38
 
37
39
  /**
38
40
  * @api {DELETE} /token user logout
package/src/logic/user.js CHANGED
@@ -33,7 +33,9 @@ module.exports = class extends Base {
33
33
  * @apiSuccess (200) {Number} errno 0
34
34
  * @apiSuccess (200) {String} errmsg return error message if error
35
35
  */
36
- postAction() {}
36
+ postAction() {
37
+ return this.useCaptchaCheck();
38
+ }
37
39
 
38
40
  /**
39
41
  * @api {PUT} /user update user profile
@@ -12,6 +12,7 @@ module.exports = function () {
12
12
  <script>
13
13
  window.SITE_URL = ${JSON.stringify(process.env.SITE_URL)};
14
14
  window.SITE_NAME = ${JSON.stringify(process.env.SITE_NAME)};
15
+ window.recaptchaV3Key = ${JSON.stringify(process.env.RECAPTCHA_V3_KEY)};
15
16
  </script>
16
17
  <script src="${
17
18
  process.env.WALINE_ADMIN_MODULE_ASSET_URL || '//unpkg.com/@waline/admin'
@@ -67,7 +67,7 @@ module.exports = class extends Base {
67
67
  return instance.count();
68
68
  }
69
69
 
70
- instance.field([...group, 'COUNT(*) as count']);
70
+ instance.field([...group, 'COUNT(*) as count'].join(','));
71
71
  instance.group(group);
72
72
 
73
73
  return instance.select();
@@ -79,10 +79,9 @@ module.exports = class extends Base {
79
79
  delete data.objectId;
80
80
  }
81
81
  const date = new Date();
82
- if (!data.createdAt)
83
- data.createdAt = date;
84
- if (!data.updatedAt)
85
- data.updatedAt = date;
82
+
83
+ if (!data.createdAt) data.createdAt = date;
84
+ if (!data.updatedAt) data.updatedAt = date;
86
85
 
87
86
  const instance = this.model(this.tableName);
88
87
  const id = await instance.add(data);