@waline/vercel 1.24.1 → 1.25.1

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.24.1",
3
+ "version": "1.25.1",
4
4
  "description": "vercel server for waline comment system",
5
5
  "keywords": [
6
6
  "waline",
@@ -19,14 +19,14 @@
19
19
  "@koa/cors": "4.0.0",
20
20
  "akismet": "2.0.7",
21
21
  "deta": "1.1.0",
22
- "dompurify": "2.4.0",
22
+ "dompurify": "2.4.1",
23
23
  "dy-node-ip2region": "1.0.1",
24
24
  "fast-csv": "4.3.6",
25
25
  "form-data": "4.0.0",
26
- "jsdom": "20.0.1",
26
+ "jsdom": "20.0.2",
27
27
  "jsonwebtoken": "8.5.1",
28
- "katex": "0.16.2",
29
- "leancloud-storage": "4.13.2",
28
+ "katex": "0.16.3",
29
+ "leancloud-storage": "4.13.4",
30
30
  "markdown-it": "13.0.1",
31
31
  "markdown-it-emoji": "2.0.2",
32
32
  "markdown-it-sub": "1.0.0",
@@ -38,6 +38,7 @@ const {
38
38
  TG_TEMPLATE,
39
39
  WX_TEMPLATE,
40
40
  DISCORD_TEMPLATE,
41
+ LARK_TEMPLATE,
41
42
 
42
43
  LEVELS,
43
44
  } = process.env;
@@ -124,4 +125,5 @@ module.exports = {
124
125
  TGTemplate: TG_TEMPLATE,
125
126
  WXTemplate: WX_TEMPLATE,
126
127
  DiscordTemplate: DISCORD_TEMPLATE,
128
+ LarkTemplate: LARK_TEMPLATE,
127
129
  };
@@ -57,7 +57,7 @@ async function formatCmt(
57
57
  }
58
58
  comment.comment = markdownParser(comment.comment);
59
59
  comment.like = Number(comment.like) || 0;
60
-
60
+
61
61
  // compat sql storage return number flag to string
62
62
  if (typeof comment.sticky === 'string') {
63
63
  comment.sticky = Boolean(Number(comment.sticky));
@@ -538,13 +538,9 @@ module.exports = class extends BaseRest {
538
538
  think.logger.debug(`Comment post frequence check OK!`);
539
539
 
540
540
  /** Akismet */
541
- const { COMMENT_AUDIT, AUTHOR_EMAIL } = process.env;
542
- const AUTHOR = AUTHOR_EMAIL;
543
- const isAuthorComment = AUTHOR
544
- ? data.mail.toLowerCase() === AUTHOR.toLowerCase()
545
- : false;
541
+ const { COMMENT_AUDIT } = process.env;
546
542
 
547
- data.status = COMMENT_AUDIT && !isAuthorComment ? 'waiting' : 'approved';
543
+ data.status = COMMENT_AUDIT ? 'waiting' : 'approved';
548
544
 
549
545
  think.logger.debug(`Comment initial status is ${data.status}`);
550
546
 
@@ -14,7 +14,7 @@ module.exports = {
14
14
  },
15
15
  locale(message, variables) {
16
16
  const { lang } = this.get();
17
- const locale = locales[(lang || '').toLowerCase()];
17
+ const locale = locales[(lang || 'zh-cn').toLowerCase()] || locales['zh-cn'];
18
18
 
19
19
  if (locale && locale[message]) {
20
20
  message = locale[message];
@@ -20,8 +20,9 @@ module.exports = class extends Base {
20
20
  *
21
21
  * @apiParam {String} path comment url path
22
22
  * @apiParam {String} page page
23
- * @apiParam {String} pagesize page size
23
+ * @apiParam {String} pageSize page size
24
24
  * @apiParam {String} sortBy comment sort type, one of 'insertedAt_desc', 'insertedAt_asc', 'like_desc'
25
+ * @apiParam {String} lang language
25
26
  *
26
27
  * @apiSuccess (200) {Number} page return current comments list page
27
28
  * @apiSuccess (200) {Number} pageSize to return error message if error
@@ -52,7 +53,8 @@ module.exports = class extends Base {
52
53
  * @apiVersion 0.0.1
53
54
  *
54
55
  * @apiParam {String} page page
55
- * @apiParam {String} pagesize page size
56
+ * @apiParam {String} pageSize page size
57
+ * @apiParam {String} lang language
56
58
  *
57
59
  * @apiSuccess (200) {Number} errno 0
58
60
  * @apiSuccess (200) {String} errmsg return error message if error
@@ -80,6 +82,7 @@ module.exports = class extends Base {
80
82
  * @apiVersion 0.0.1
81
83
  *
82
84
  * @apiParam {String} url a array string join by comma just like `a` or `a,b`, return site comment count if url empty
85
+ * @apiParam {String} lang language
83
86
  *
84
87
  * @apiSuccessExample {Number} Single Path Response:
85
88
  * 300
@@ -92,6 +95,7 @@ module.exports = class extends Base {
92
95
  * @apiVersion 0.0.1
93
96
  *
94
97
  * @apiParam {String} count return comments number, default value is 10
98
+ * @apiParam {String} lang language
95
99
  *
96
100
  * @apiSuccess (200) {Object[]} response
97
101
  * @apiSuccess (200) {String} response.nick comment user nick name
@@ -181,11 +185,12 @@ module.exports = class extends Base {
181
185
  * @apiParam {String} mail post comment user mail address
182
186
  * @apiParam {String} link post comment user link
183
187
  * @apiParam {String} comment post comment text
184
- * @apiParam {String} url the artcile url path of comment
188
+ * @apiParam {String} url the article url path of comment
185
189
  * @apiParam {String} ua browser user agent
186
190
  * @apiParam {String} pid parent comment id
187
191
  * @apiParam {String} rid root comment id
188
192
  * @apiParam {String} at parent comment user nick name
193
+ * @apiParam {String} lang language
189
194
  *
190
195
  * @apiSuccess (200) {Number} errno 0
191
196
  * @apiSuccess (200) {String} errmsg return error message if error
@@ -224,8 +229,9 @@ module.exports = class extends Base {
224
229
  * @apiParam {String} [mail] post comment user mail address
225
230
  * @apiParam {String} [link] post comment user link
226
231
  * @apiParam {String} [comment] post comment text
227
- * @apiParam {String} [url] the artcile url path of comment
232
+ * @apiParam {String} [url] the article url path of comment
228
233
  * @apiParam {Boolean} [like] like comment
234
+ * @apiParam {String} lang language
229
235
  *
230
236
  * @apiSuccess (200) {Number} errno 0
231
237
  * @apiSuccess (200) {String} errmsg return error message if error
@@ -277,6 +283,8 @@ module.exports = class extends Base {
277
283
  * @apiGroup Comment
278
284
  * @apiVersion 0.0.1
279
285
  *
286
+ * @apiParam {String} lang language
287
+ *
280
288
  * @apiSuccess (200) {Number} errno 0
281
289
  * @apiSuccess (200) {String} errmsg return error message if error
282
290
  */
package/src/logic/db.js CHANGED
@@ -19,6 +19,8 @@ module.exports = class extends Base {
19
19
  * @api {GET} /db export site data
20
20
  * @apiGroup Site
21
21
  * @apiVersion 0.0.1
22
+ *
23
+ * @apiParam {String} lang language
22
24
  */
23
25
  async getAction() {}
24
26
 
@@ -26,6 +28,8 @@ module.exports = class extends Base {
26
28
  * @api {POST} /db import site data
27
29
  * @apiGroup Site
28
30
  * @apiVersion 0.0.1
31
+ *
32
+ * @apiParam {String} lang language
29
33
  */
30
34
  async postAction() {
31
35
  this.rules = {
@@ -41,6 +45,8 @@ module.exports = class extends Base {
41
45
  * @api {PUT} /db update site table data
42
46
  * @apiGroup Site
43
47
  * @apiVersion 0.0.1
48
+ *
49
+ * @apiParam {String} lang language
44
50
  */
45
51
  async putAction() {
46
52
  this.rules = {
@@ -60,6 +66,8 @@ module.exports = class extends Base {
60
66
  * @api {DELETE} /db clean site data
61
67
  * @apiGroup Site
62
68
  * @apiVersion 0.0.1
69
+ *
70
+ * @apiParam {String} lang language
63
71
  */
64
72
  async deleteAction() {
65
73
  this.rules = {
@@ -6,6 +6,8 @@ module.exports = class extends Base {
6
6
  * @apiGroup User
7
7
  * @apiVersion 0.0.1
8
8
  *
9
+ * @apiParam {String} lang language
10
+ *
9
11
  * @apiSuccess (200) {Number} errno 0
10
12
  * @apiSuccess (200) {String} errmsg return error message if error
11
13
  * @apiSuccess (200) {Object} data user info
@@ -28,6 +30,7 @@ module.exports = class extends Base {
28
30
  *
29
31
  * @apiParam {String} email login user email
30
32
  * @apiParam {String} password login user password
33
+ * @apiParam {String} lang language
31
34
  *
32
35
  * @apiSuccess (200) {Number} errno 0
33
36
  * @apiSuccess (200) {String} errmsg return error message if error
@@ -41,6 +44,8 @@ module.exports = class extends Base {
41
44
  * @apiGroup User
42
45
  * @apiVersion 0.0.1
43
46
  *
47
+ * @apiParam {String} lang language
48
+ *
44
49
  * @apiSuccess (200) {Number} errno 0
45
50
  * @apiSuccess (200) {String} errmsg return error message if error
46
51
  */
package/src/logic/user.js CHANGED
@@ -2,14 +2,43 @@ const Base = require('./base');
2
2
 
3
3
  module.exports = class extends Base {
4
4
  /**
5
- * @api {GET} /user user list
5
+ * @api {GET} /user user top list without admin
6
6
  * @apiGroup User
7
7
  * @apiVersion 0.0.1
8
8
  *
9
9
  * @apiParam {String} pageSize page size
10
+ * @apiParam {String} lang language
10
11
  *
11
12
  * @apiSuccess (200) {Number} errno 0
12
13
  * @apiSuccess (200) {String} errmsg return error message if error
14
+ * @apiSuccess (200) {Object[]} data user list
15
+ * @apiSuccess (200) {String} data.nick comment user nick name
16
+ * @apiSuccess (200) {String} data.link comment user link
17
+ * @apiSuccess (200) {String} data.avatar comment user avatar
18
+ * @apiSuccess (200) {String} data.level comment user level
19
+ * @apiSuccess (200) {String} data.count user comment count
20
+ */
21
+ /**
22
+ * @api {GET} /user?token user list with admin login
23
+ * @apiGroup User
24
+ * @apiVersion 0.0.1
25
+ *
26
+ * @apiParam {String} page page
27
+ * @apiParam {String} pageSize page size
28
+ * @apiParam {String} lang language
29
+ *
30
+ * @apiSuccess (200) {Number} errno 0
31
+ * @apiSuccess (200) {String} errmsg return error message if error
32
+ * @apiSuccess (200) {Object} data user list
33
+ * @apiSuccess (200) {Number} data.page user list current page
34
+ * @apiSuccess (200) {Number} data.pageSize user list page size
35
+ * @apiSuccess (200) {Number} data.totalPages user list total pages
36
+ * @apiSuccess (200) {Object[]} data.data user list data
37
+ * @apiSuccess (200) {String} data.data.nick comment user nick name
38
+ * @apiSuccess (200) {String} data.data.link comment user link
39
+ * @apiSuccess (200) {String} data.data.avatar comment user avatar
40
+ * @apiSuccess (200) {String} data.data.level comment user level
41
+ * @apiSuccess (200) {String} data.data.label comment user label
13
42
  */
14
43
  getAction() {
15
44
  const { userInfo } = this.ctx.state;
@@ -49,15 +78,10 @@ module.exports = class extends Base {
49
78
  * @apiParam {String} email user email
50
79
  * @apiParam {String} password user password
51
80
  * @apiParam {String} url user link
81
+ * @apiParam {String} lang language
52
82
  *
53
83
  * @apiSuccess (200) {Number} errno 0
54
84
  * @apiSuccess (200) {String} errmsg return error message if error
55
- * @apiSuccess (200) {Object[]} data user list
56
- * @apiSuccess (200) {String} data.nick comment user nick name
57
- * @apiSuccess (200) {String} data.link comment user link
58
- * @apiSuccess (200) {String} data.avatar comment user avatar
59
- * @apiSuccess (200) {String} data.level comment user level
60
- * @apiSuccess (200) {String} data.label comment user label
61
85
  */
62
86
  postAction() {
63
87
  return this.useCaptchaCheck();
@@ -72,6 +96,7 @@ module.exports = class extends Base {
72
96
  * @apiParam {String} [url] user new link
73
97
  * @apiParam {String} [password] user new password
74
98
  * @apiParam {String} [github] user github account name
99
+ * @apiParam {String} lang language
75
100
  *
76
101
  * @apiSuccess (200) {Number} errno 0
77
102
  * @apiSuccess (200) {String} errmsg return error message if error
@@ -14,7 +14,7 @@ const getMarkdownParser = () => {
14
14
  // markdown-it instance
15
15
  const markdownIt = MarkdownIt({
16
16
  breaks: true,
17
- linkify: true, // Autoconvert URL-like text to links
17
+ linkify: true, // Auto convert URL-like text to links
18
18
  typographer: true, // Enable some language-neutral replacement + quotes beautification
19
19
 
20
20
  // default highlight
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Test if potential opening or closing delimieter
2
+ * Test if potential opening or closing delimiter
3
3
  * Assumes that there is a "$" at state.src[pos]
4
4
  */
5
5
  const isValidDelim = (state, pos) => {
@@ -10,7 +10,7 @@ const isValidDelim = (state, pos) => {
10
10
  canOpen: nextChar !== ' ' && nextChar !== '\t',
11
11
  /*
12
12
  * Check non-whitespace conditions for opening and closing, and
13
- * check that closing delimeter isn’t followed by a number
13
+ * check that closing delimiter isn’t followed by a number
14
14
  */
15
15
  canClose: !(
16
16
  prevChar === ' ' ||
@@ -36,10 +36,10 @@ const inlineTex = (state, silent) => {
36
36
  return true;
37
37
  }
38
38
  /*
39
- * First check for and bypass all properly escaped delimieters
39
+ * First check for and bypass all properly escaped delimiters
40
40
  * This loop will assume that the first leading backtick can not
41
41
  * be the first character in state.src, which is known since
42
- * we have found an opening delimieter already.
42
+ * we have found an opening delimiter already.
43
43
  */
44
44
  const start = state.pos + 1;
45
45
 
@@ -56,7 +56,7 @@ const inlineTex = (state, silent) => {
56
56
  match += 1;
57
57
  }
58
58
 
59
- // No closing delimter found. Consume $ and continue.
59
+ // No closing delimiter found. Consume $ and continue.
60
60
  if (match === -1) {
61
61
  if (!silent) state.pending += '$';
62
62
  state.pos = start;
@@ -8,7 +8,7 @@ const { AllPackages } = require('mathjax-full/js/input/tex/AllPackages.js');
8
8
  const { escapeHtml } = require('./utils');
9
9
  const { inlineTex, blockTex } = require('./mathCommon');
10
10
 
11
- // set MathJax as the renderer for markdown-it-simplemath
11
+ // set MathJax as the renderer
12
12
  class MathToSvg {
13
13
  constructor() {
14
14
  const adaptor = liteAdaptor();
@@ -29,7 +29,7 @@ class MathToSvg {
29
29
  if (svg.includes('data-mml-node="merror"')) {
30
30
  const errorTitle = svg.match(/<title>(.*?)<\/title>/)[1];
31
31
 
32
- svg = `<span class='katex-error' title='${escapeHtml(
32
+ svg = `<span class='mathjax-error' title='${escapeHtml(
33
33
  errorTitle
34
34
  )}'>${escapeHtml(tex)}</span>`;
35
35
  }
@@ -44,7 +44,7 @@ class MathToSvg {
44
44
  if (svg.includes('data-mml-node="merror"')) {
45
45
  const errorTitle = svg.match(/<title>(.*?)<\/title>/)[1];
46
46
 
47
- svg = `<p class='katex-block katex-error' title='${escapeHtml(
47
+ svg = `<p class='mathjax-block mathjax-error' title='${escapeHtml(
48
48
  errorTitle
49
49
  )}'>${escapeHtml(tex)}</p>`;
50
50
  } else {
@@ -2,6 +2,7 @@ const FormData = require('form-data');
2
2
  const nodemailer = require('nodemailer');
3
3
  const fetch = require('node-fetch');
4
4
  const nunjucks = require('nunjucks');
5
+ const crypto = require('crypto');
5
6
 
6
7
  module.exports = class extends think.Service {
7
8
  constructor(ctx) {
@@ -85,8 +86,8 @@ module.exports = class extends think.Service {
85
86
  },
86
87
  };
87
88
 
88
- title = nunjucks.renderString(title, data);
89
- content = nunjucks.renderString(content, data);
89
+ title = this.ctx.locale(title, data);
90
+ content = this.ctx.locale(content, data);
90
91
 
91
92
  const form = new FormData();
92
93
 
@@ -134,8 +135,8 @@ module.exports = class extends think.Service {
134
135
  【内容】:{{self.comment}}
135
136
  <a href='{{site.postUrl}}'>查看详情</a>`;
136
137
 
137
- title = nunjucks.renderString(title, data);
138
- const desp = nunjucks.renderString(contentWechat, data);
138
+ title = this.ctx.locale(title, data);
139
+ const desp = this.ctx.locale(contentWechat, data);
139
140
 
140
141
  content = desp.replace(/\n/g, '<br/>');
141
142
 
@@ -214,7 +215,7 @@ module.exports = class extends think.Service {
214
215
 
215
216
  const form = new FormData();
216
217
 
217
- form.append('msg', nunjucks.renderString(contentQQ, data));
218
+ form.append('msg', this.ctx.locale(contentQQ, data));
218
219
  form.append('qq', QQ_ID);
219
220
 
220
221
  return fetch(`https://qmsg.zendee.cn/send/${QMSG_KEY}`, {
@@ -283,7 +284,7 @@ module.exports = class extends think.Service {
283
284
 
284
285
  const form = new FormData();
285
286
 
286
- form.append('text', nunjucks.renderString(contentTG, data));
287
+ form.append('text', this.ctx.locale(contentTG, data));
287
288
  form.append('chat_id', TG_CHAT_ID);
288
289
  form.append('parse_mode', 'MarkdownV2');
289
290
 
@@ -297,7 +298,7 @@ module.exports = class extends think.Service {
297
298
  ).then((resp) => resp.json());
298
299
 
299
300
  if (!resp.ok) {
300
- throw new Error(resp.description);
301
+ console.log('Telegram Notification Failed:' + JSON.stringify(resp));
301
302
  }
302
303
  }
303
304
 
@@ -327,18 +328,18 @@ module.exports = class extends think.Service {
327
328
  },
328
329
  };
329
330
 
330
- title = nunjucks.renderString(title, data);
331
- content = nunjucks.renderString(content, data);
331
+ title = this.ctx.locale(title, data);
332
+ content = this.ctx.locale(content, data);
332
333
 
333
334
  const form = new FormData();
334
335
 
335
- form.append('topic', topic);
336
- form.append('template', template);
337
- form.append('channel', channel);
338
- form.append('webhook', webhook);
339
- form.append('callbackUrl', callbackUrl);
340
- form.append('title', title);
341
- form.append('content', content);
336
+ topic && form.append('topic', topic);
337
+ template && form.append('template', template);
338
+ channel && form.append('channel', channel);
339
+ webhook && form.append('webhook', webhook);
340
+ callbackUrl && form.append('callbackUrl', callbackUrl);
341
+ title && form.append('title', title);
342
+ content && form.append('content', content);
342
343
 
343
344
  return fetch(`http://www.pushplus.plus/send/${PUSH_PLUS_KEY}`, {
344
345
  method: 'POST',
@@ -364,8 +365,8 @@ module.exports = class extends think.Service {
364
365
  },
365
366
  };
366
367
 
367
- title = nunjucks.renderString(title, data);
368
- content = nunjucks.renderString(
368
+ title = this.ctx.locale(title, data);
369
+ content = this.ctx.locale(
369
370
  think.config('DiscordTemplate') ||
370
371
  `💬 {{site.name|safe}} 有新评论啦
371
372
  【评论者昵称】:{{self.nick}}
@@ -386,6 +387,83 @@ module.exports = class extends think.Service {
386
387
  }).then((resp) => resp.json());
387
388
  }
388
389
 
390
+ async lark({ title, content }, self, parent) {
391
+ const { LARK_WEBHOOK, LARK_SECRET, SITE_NAME, SITE_URL } = process.env;
392
+
393
+ if (!LARK_WEBHOOK) {
394
+ return false;
395
+ }
396
+
397
+ self.comment = self.comment.replace(/(<([^>]+)>)/gi, '');
398
+
399
+ const data = {
400
+ self,
401
+ parent,
402
+ site: {
403
+ name: SITE_NAME,
404
+ url: SITE_URL,
405
+ postUrl: SITE_URL + self.url + '#' + self.objectId,
406
+ },
407
+ };
408
+
409
+ content = nunjucks.renderString(
410
+ think.config('LarkTemplate') ||
411
+ `【网站名称】:{{site.name|safe}} \n【评论者昵称】:{{self.nick}}\n【评论者邮箱】:{{self.mail}}\n【内容】:{{self.comment}}【地址】:{{site.postUrl}}`,
412
+ data
413
+ );
414
+
415
+ const post = {
416
+ en_us: {
417
+ title: this.ctx.locale(title, data),
418
+ content: [
419
+ [
420
+ {
421
+ tag: 'text',
422
+ text: content,
423
+ },
424
+ ],
425
+ ],
426
+ },
427
+ };
428
+
429
+ let signData = {};
430
+ const msg = {
431
+ msg_type: 'post',
432
+ content: {
433
+ post,
434
+ },
435
+ };
436
+
437
+ const sign = (timestamp, secret) => {
438
+ const signStr = timestamp + '\n' + secret;
439
+
440
+ return crypto.createHmac('sha256', signStr).update('').digest('base64');
441
+ };
442
+
443
+ if (LARK_SECRET) {
444
+ const timestamp = parseInt(+new Date() / 1000);
445
+
446
+ signData = { timestamp: timestamp, sign: sign(timestamp, LARK_SECRET) };
447
+ }
448
+
449
+ const resp = await fetch(LARK_WEBHOOK, {
450
+ method: 'POST',
451
+ headers: {
452
+ 'Content-Type': 'application/json',
453
+ },
454
+ body: JSON.stringify({
455
+ ...signData,
456
+ ...msg,
457
+ }),
458
+ }).then((resp) => resp.json());
459
+
460
+ if (resp.status !== 200) {
461
+ console.log('Lark Notification Failed:' + JSON.stringify(resp));
462
+ }
463
+
464
+ console.log('FeiShu Notification Success:' + JSON.stringify(resp));
465
+ }
466
+
389
467
  async run(comment, parent, disableAuthorNotify = false) {
390
468
  const { AUTHOR_EMAIL, DISABLE_AUTHOR_NOTIFY } = process.env;
391
469
  const { mailSubject, mailTemplate, mailSubjectAdmin, mailTemplateAdmin } =
@@ -416,9 +494,10 @@ module.exports = class extends think.Service {
416
494
  const telegram = await this.telegram(comment, parent);
417
495
  const pushplus = await this.pushplus({ title, content }, comment, parent);
418
496
  const discord = await this.discord({ title, content }, comment, parent);
497
+ const lark = await this.lark({ title, content }, comment, parent);
419
498
 
420
499
  if (
421
- [wechat, qq, telegram, qywxAmWechat, pushplus, discord].every(
500
+ [wechat, qq, telegram, qywxAmWechat, pushplus, discord, lark].every(
422
501
  think.isEmpty
423
502
  ) &&
424
503
  !isReplyAuthor