@waline/vercel 1.18.8 → 1.19.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.
@@ -12,18 +12,21 @@ module.exports = class extends BaseRest {
12
12
  async getAction() {
13
13
  const { token, email } = this.get();
14
14
  const users = await this.modelInstance.select({ email });
15
+
15
16
  if (think.isEmpty(users)) {
16
17
  return this.fail(this.locale('USER_NOT_EXIST'));
17
18
  }
18
19
 
19
20
  const user = users[0];
20
21
  const match = user.type.match(/^verify:(\d{4}):(\d+)$/i);
22
+
21
23
  if (!match) {
22
24
  return this.fail(this.locale('USER_REGISTED'));
23
25
  }
24
26
 
25
27
  if (token === match[1] && Date.now() < parseInt(match[2])) {
26
28
  await this.modelInstance.update({ type: 'guest' }, { email });
29
+
27
30
  return this.redirect('/ui/login');
28
31
  }
29
32
 
@@ -4,18 +4,22 @@ const locales = require('../locales');
4
4
  module.exports = {
5
5
  success(...args) {
6
6
  this.ctx.success(...args);
7
+
7
8
  return think.prevent();
8
9
  },
9
10
  fail(...args) {
10
11
  this.ctx.fail(...args);
12
+
11
13
  return think.prevent();
12
14
  },
13
15
  locale(message, variables) {
14
16
  const { lang } = this.get();
15
17
  const locale = locales[(lang || '').toLowerCase()];
18
+
16
19
  if (locale && locale[message]) {
17
20
  message = locale[message];
18
21
  }
22
+
19
23
  return nunjucks.renderString(message, variables);
20
24
  },
21
25
  };
@@ -14,9 +14,11 @@ module.exports = {
14
14
  findLastIndex(arr, fn) {
15
15
  for (let i = arr.length - 1; i >= 0; i--) {
16
16
  const ret = fn(arr[i], i, arr);
17
+
17
18
  if (!ret) {
18
19
  continue;
19
20
  }
21
+
20
22
  return i;
21
23
  }
22
24
 
@@ -34,6 +36,7 @@ module.exports = {
34
36
 
35
37
  function runTask() {
36
38
  const idx = index;
39
+
37
40
  index += 1;
38
41
  if (index > promises.length) {
39
42
  return Promise.resolve();
@@ -45,6 +48,7 @@ module.exports = {
45
48
  if (count === promises.length) {
46
49
  resolve(ret);
47
50
  }
51
+
48
52
  return runTask();
49
53
  }, reject);
50
54
  }
@@ -55,20 +59,23 @@ module.exports = {
55
59
  });
56
60
  },
57
61
  async ip2region(ip, { depth = 1 }) {
58
- if (!ip) return '';
62
+ if (!ip || ip.includes(':')) return '';
59
63
 
60
64
  try {
61
65
  const search = helper.promisify(regionSearch.btreeSearch, regionSearch);
62
66
  const result = await search(ip);
67
+
63
68
  if (!result) {
64
69
  return '';
65
70
  }
66
71
  const { region } = result;
67
72
  const [, , province, city, isp] = region.split('|');
68
73
  const address = Array.from(new Set([province, city, isp]));
74
+
69
75
  return address.slice(0, depth).join(' ');
70
76
  } catch (e) {
71
77
  console.log(e);
78
+
72
79
  return '';
73
80
  }
74
81
  },
package/src/logic/base.js CHANGED
@@ -16,6 +16,7 @@ module.exports = class extends think.Logic {
16
16
  async __before() {
17
17
  const referrer = this.ctx.referrer(true);
18
18
  let { secureDomains } = this.config();
19
+
19
20
  if (secureDomains && referrer && this.ctx.host.indexOf(referrer) !== 0) {
20
21
  secureDomains = think.isArray(secureDomains)
21
22
  ? secureDomains
@@ -33,6 +34,7 @@ module.exports = class extends think.Logic {
33
34
  ? domain.test(referrer)
34
35
  : domain === referrer
35
36
  );
37
+
36
38
  if (!match) {
37
39
  return this.ctx.throw(403);
38
40
  }
@@ -41,11 +43,13 @@ module.exports = class extends think.Logic {
41
43
  this.ctx.state.userInfo = {};
42
44
  const { authorization } = this.ctx.req.headers;
43
45
  const { state } = this.get();
46
+
44
47
  if (!authorization && !state) {
45
48
  return;
46
49
  }
47
50
  const token = state || authorization.replace(/^Bearer /, '');
48
51
  const userMail = jwt.verify(token, think.config('jwtKey'));
52
+
49
53
  if (think.isEmpty(userMail) || !think.isString(userMail)) {
50
54
  return;
51
55
  }
@@ -71,6 +75,7 @@ module.exports = class extends think.Logic {
71
75
  ],
72
76
  }
73
77
  );
78
+
74
79
  if (think.isEmpty(user)) {
75
80
  return;
76
81
  }
@@ -85,6 +90,7 @@ module.exports = class extends think.Logic {
85
90
  link: userInfo.url,
86
91
  });
87
92
  const { avatarProxy } = think.config();
93
+
88
94
  if (avatarProxy) {
89
95
  avatarUrl = avatarProxy + '?url=' + encodeURIComponent(avatarUrl);
90
96
  }
@@ -1,4 +1,5 @@
1
1
  const Base = require('./base');
2
+
2
3
  module.exports = class extends Base {
3
4
  async __before() {
4
5
  await super.__before();
@@ -7,6 +8,7 @@ module.exports = class extends Base {
7
8
  const { like } = this.post();
8
9
  const isAllowedGet = this.isGet && (type !== 'list' || path);
9
10
  const isAllowedPut = this.ctx.isMethod('PUT') && think.isBoolean(like);
11
+
10
12
  if (this.isPost || isAllowedGet || isAllowedPut) {
11
13
  return;
12
14
  }
@@ -114,6 +116,7 @@ module.exports = class extends Base {
114
116
  */
115
117
  getAction() {
116
118
  const { type } = this.get();
119
+
117
120
  switch (type) {
118
121
  case 'recent':
119
122
  this.rules = {
@@ -135,6 +138,7 @@ module.exports = class extends Base {
135
138
 
136
139
  case 'list': {
137
140
  const { userInfo } = this.ctx.state;
141
+
138
142
  if (userInfo.type !== 'administrator') {
139
143
  return this.fail();
140
144
  }
@@ -224,6 +228,7 @@ module.exports = class extends Base {
224
228
  */
225
229
  putAction() {
226
230
  const { userInfo } = this.ctx.state;
231
+
227
232
  if (think.isEmpty(userInfo) || userInfo.type !== 'administrator') {
228
233
  this.rules = {
229
234
  like: {
package/src/logic/db.js CHANGED
@@ -5,6 +5,7 @@ module.exports = class extends Base {
5
5
  await super.__before(...args);
6
6
 
7
7
  const { userInfo } = this.ctx.state;
8
+
8
9
  if (think.isEmpty(userInfo)) {
9
10
  return this.fail(401);
10
11
  }
@@ -12,6 +12,7 @@ module.exports = class extends Base {
12
12
 
13
13
  async postAction() {
14
14
  const { userInfo } = this.ctx.state;
15
+
15
16
  if (think.isEmpty(userInfo)) {
16
17
  return this.fail(401);
17
18
  }
package/src/logic/user.js CHANGED
@@ -3,6 +3,7 @@ const Base = require('./base');
3
3
  module.exports = class extends Base {
4
4
  getAction() {
5
5
  const { userInfo } = this.ctx.state;
6
+
6
7
  if (think.isEmpty(userInfo) || userInfo.type !== 'administrator') {
7
8
  return this.fail();
8
9
  }
@@ -50,6 +51,7 @@ module.exports = class extends Base {
50
51
  putAction() {
51
52
  // you need login to update yourself profile
52
53
  const { userInfo } = this.ctx.state;
54
+
53
55
  if (think.isEmpty(userInfo)) {
54
56
  return this.fail();
55
57
  }
@@ -3,6 +3,7 @@ const DEFAULT_KEY = '70542d86693e';
3
3
 
4
4
  module.exports = function (comment, blog) {
5
5
  let { AKISMET_KEY, SITE_URL } = process.env;
6
+
6
7
  if (!AKISMET_KEY) {
7
8
  AKISMET_KEY = DEFAULT_KEY;
8
9
  }
@@ -3,6 +3,7 @@ const helper = require('think-helper');
3
3
  const { GRAVATAR_STR } = process.env;
4
4
 
5
5
  const env = new nunjucks.Environment();
6
+
6
7
  env.addFilter('md5', (str) => helper.md5(str));
7
8
 
8
9
  const DEFAULT_GRAVATAR_STR = `{%- set numExp = r/^[0-9]+$/g -%}
@@ -14,17 +15,21 @@ const DEFAULT_GRAVATAR_STR = `{%- set numExp = r/^[0-9]+$/g -%}
14
15
  {%- else -%}
15
16
  https://seccdn.libravatar.org/avatar/{{mail|md5}}
16
17
  {%- endif -%}`;
18
+
17
19
  module.exports = class extends think.Service {
18
20
  async stringify(comment) {
19
21
  const fn = think.config('avatarUrl');
22
+
20
23
  if (think.isFunction(fn)) {
21
24
  const ret = await fn(comment);
25
+
22
26
  if (think.isString(ret) && ret) {
23
27
  return ret;
24
28
  }
25
29
  }
26
30
 
27
31
  const gravatarStr = GRAVATAR_STR || DEFAULT_GRAVATAR_STR;
32
+
28
33
  return env.renderString(gravatarStr, comment);
29
34
  }
30
35
  };
@@ -9,6 +9,7 @@ const katexInline = (tex, options) => {
9
9
  return katex.renderToString(tex, options);
10
10
  } catch (error) {
11
11
  if (options.throwOnError) console.warn(error);
12
+
12
13
  return `<span class='katex-error' title='${escapeHtml(
13
14
  error.toString()
14
15
  )}'>${escapeHtml(tex)}</span>`;
@@ -21,6 +22,7 @@ const katexBlock = (tex, options) => {
21
22
  return `<p class='katex-block'>${katex.renderToString(tex, options)}</p>`;
22
23
  } catch (error) {
23
24
  if (options.throwOnError) console.warn(error);
25
+
24
26
  return `<p class='katex-block katex-error' title='${escapeHtml(
25
27
  error.toString()
26
28
  )}'>${escapeHtml(tex)}</p>`;
@@ -5,6 +5,7 @@
5
5
  const isValidDelim = (state, pos) => {
6
6
  const prevChar = pos > 0 ? state.src.charAt(pos - 1) : '';
7
7
  const nextChar = pos + 1 <= state.posMax ? state.src.charAt(pos + 1) : '';
8
+
8
9
  return {
9
10
  canOpen: nextChar !== ' ' && nextChar !== '\t',
10
11
  /*
@@ -31,6 +32,7 @@ const inlineTex = (state, silent) => {
31
32
  if (!res.canOpen) {
32
33
  if (!silent) state.pending += '$';
33
34
  state.pos += 1;
35
+
34
36
  return true;
35
37
  }
36
38
  /*
@@ -58,6 +60,7 @@ const inlineTex = (state, silent) => {
58
60
  if (match === -1) {
59
61
  if (!silent) state.pending += '$';
60
62
  state.pos = start;
63
+
61
64
  return true;
62
65
  }
63
66
 
@@ -65,6 +68,7 @@ const inlineTex = (state, silent) => {
65
68
  if (match - start === 0) {
66
69
  if (!silent) state.pending += '$$';
67
70
  state.pos = start + 1;
71
+
68
72
  return true;
69
73
  }
70
74
 
@@ -74,6 +78,7 @@ const inlineTex = (state, silent) => {
74
78
  if (!res.canClose) {
75
79
  if (!silent) state.pending += '$';
76
80
  state.pos = start;
81
+
77
82
  return true;
78
83
  }
79
84
 
@@ -12,6 +12,7 @@ const { inlineTex, blockTex } = require('./mathCommon');
12
12
  class MathToSvg {
13
13
  constructor() {
14
14
  const adaptor = liteAdaptor();
15
+
15
16
  RegisterHTMLHandler(adaptor);
16
17
 
17
18
  const packages = AllPackages.sort();
@@ -27,6 +28,7 @@ class MathToSvg {
27
28
 
28
29
  if (svg.includes('data-mml-node="merror"')) {
29
30
  const errorTitle = svg.match(/<title>(.*?)<\/title>/)[1];
31
+
30
32
  svg = `<span class='katex-error' title='${escapeHtml(
31
33
  errorTitle
32
34
  )}'>${escapeHtml(tex)}</span>`;
@@ -41,6 +43,7 @@ class MathToSvg {
41
43
 
42
44
  if (svg.includes('data-mml-node="merror"')) {
43
45
  const errorTitle = svg.match(/<title>(.*?)<\/title>/)[1];
46
+
44
47
  svg = `<p class='katex-block katex-error' title='${escapeHtml(
45
48
  errorTitle
46
49
  )}'>${escapeHtml(tex)}</p>`;
@@ -1,6 +1,8 @@
1
+ const FormData = require('form-data');
1
2
  const nodemailer = require('nodemailer');
3
+ const fetch = require('node-fetch');
2
4
  const nunjucks = require('nunjucks');
3
- const request = require('request-promise-native');
5
+
4
6
  module.exports = class extends think.Service {
5
7
  constructor(...args) {
6
8
  super(...args);
@@ -13,10 +15,12 @@ module.exports = class extends think.Service {
13
15
  SMTP_SECURE,
14
16
  SMTP_SERVICE,
15
17
  } = process.env;
18
+
16
19
  if (SMTP_HOST || SMTP_SERVICE) {
17
20
  const config = {
18
21
  auth: { user: SMTP_USER, pass: SMTP_PASS },
19
22
  };
23
+
20
24
  if (SMTP_SERVICE) {
21
25
  config.service = SMTP_SERVICE;
22
26
  } else {
@@ -48,6 +52,7 @@ module.exports = class extends think.Service {
48
52
  postUrl: SITE_URL + self.url + '#' + self.objectId,
49
53
  },
50
54
  };
55
+
51
56
  title = nunjucks.renderString(title, data);
52
57
  content = nunjucks.renderString(content, data);
53
58
 
@@ -64,6 +69,7 @@ module.exports = class extends think.Service {
64
69
 
65
70
  async wechat({ title, content }, self, parent) {
66
71
  const { SC_KEY, SITE_NAME, SITE_URL } = process.env;
72
+
67
73
  if (!SC_KEY) {
68
74
  return false;
69
75
  }
@@ -77,22 +83,25 @@ module.exports = class extends think.Service {
77
83
  postUrl: SITE_URL + self.url + '#' + self.objectId,
78
84
  },
79
85
  };
86
+
80
87
  title = nunjucks.renderString(title, data);
81
88
  content = nunjucks.renderString(content, data);
82
89
 
83
- return request({
84
- uri: `https://sctapi.ftqq.com/${SC_KEY}.send`,
90
+ const form = new FormData();
91
+
92
+ form.append('text', title);
93
+ form.append('desp', content);
94
+
95
+ return fetch(`https://sctapi.ftqq.com/${SC_KEY}.send`, {
85
96
  method: 'POST',
86
- form: {
87
- text: title,
88
- desp: content,
89
- },
90
- json: true,
91
- });
97
+ headers: form.getHeaders(),
98
+ body: form,
99
+ }).then((resp) => resp.json());
92
100
  }
93
101
 
94
102
  async qywxAmWechat({ title, content }, self, parent) {
95
103
  const { QYWX_AM, SITE_NAME, SITE_URL } = process.env;
104
+
96
105
  if (!QYWX_AM) {
97
106
  return false;
98
107
  }
@@ -126,48 +135,55 @@ module.exports = class extends think.Service {
126
135
 
127
136
  title = nunjucks.renderString(title, data);
128
137
  const desp = nunjucks.renderString(contentWechat, data);
138
+
129
139
  content = desp.replace(/\n/g, '<br/>');
130
140
 
131
- const { access_token } = await request({
132
- uri: `https://qyapi.weixin.qq.com/cgi-bin/gettoken`,
133
- qs: {
134
- corpid: `${QYWX_AM_AY[0]}`,
135
- corpsecret: `${QYWX_AM_AY[1]}`,
136
- },
137
- headers: {
138
- 'Content-Type': 'application/json',
139
- },
140
- json: true,
141
- });
142
- return request({
143
- url: `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${access_token}`,
144
- body: {
145
- touser: `${QYWX_AM_AY[2]}`,
146
- agentid: `${QYWX_AM_AY[3]}`,
147
- msgtype: 'mpnews',
148
- mpnews: {
149
- articles: [
150
- {
151
- title,
152
- thumb_media_id: `${QYWX_AM_AY[4]}`,
153
- author: `Waline Comment`,
154
- content_source_url: `${data.site.postUrl}`,
155
- content: `${content}`,
156
- digest: `${desp}`,
157
- },
158
- ],
141
+ const querystring = new URLSearchParams();
142
+
143
+ querystring.set('corpid', `${QYWX_AM_AY[0]}`);
144
+ querystring.set('corpsecret', `${QYWX_AM_AY[1]}`);
145
+
146
+ const { access_token } = await fetch(
147
+ `https://qyapi.weixin.qq.com/cgi-bin/gettoken`,
148
+ {
149
+ headers: {
150
+ 'content-type': 'application/json',
159
151
  },
160
- },
161
- method: 'POST',
162
- json: true,
163
- headers: {
164
- 'Content-Type': 'application/json',
165
- },
166
- });
152
+ body: querystring,
153
+ }
154
+ ).then((resp) => resp.json());
155
+
156
+ return fetch(
157
+ `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${access_token}`,
158
+ {
159
+ method: 'POST',
160
+ headers: {
161
+ 'content-type': 'application/json',
162
+ },
163
+ body: JSON.stringify({
164
+ touser: `${QYWX_AM_AY[2]}`,
165
+ agentid: `${QYWX_AM_AY[3]}`,
166
+ msgtype: 'mpnews',
167
+ mpnews: {
168
+ articles: [
169
+ {
170
+ title,
171
+ thumb_media_id: `${QYWX_AM_AY[4]}`,
172
+ author: `Waline Comment`,
173
+ content_source_url: `${data.site.postUrl}`,
174
+ content: `${content}`,
175
+ digest: `${desp}`,
176
+ },
177
+ ],
178
+ },
179
+ }),
180
+ }
181
+ ).then((resp) => resp.json());
167
182
  }
168
183
 
169
184
  async qq(self, parent) {
170
185
  const { QMSG_KEY, QQ_ID, SITE_NAME, SITE_URL } = process.env;
186
+
171
187
  if (!QMSG_KEY) {
172
188
  return false;
173
189
  }
@@ -196,24 +212,28 @@ module.exports = class extends think.Service {
196
212
  {{self.comment}}
197
213
  仅供预览评论,请前往上述页面查看完整內容。`;
198
214
 
199
- return request({
200
- uri: `https://qmsg.zendee.cn/send/${QMSG_KEY}`,
215
+ const form = new FormData();
216
+
217
+ form.append('msg', nunjucks.renderString(contentQQ, data));
218
+ form.append('qq', QQ_ID);
219
+
220
+ return fetch(`https://qmsg.zendee.cn/send/${QMSG_KEY}`, {
201
221
  method: 'POST',
202
- form: {
203
- msg: nunjucks.renderString(contentQQ, data),
204
- qq: QQ_ID,
205
- },
206
- });
222
+ header: form.getHeaders(),
223
+ body: form,
224
+ }).then((resp) => resp.json());
207
225
  }
208
226
 
209
227
  async telegram(self, parent) {
210
228
  const { TG_BOT_TOKEN, TG_CHAT_ID, SITE_NAME, SITE_URL } = process.env;
229
+
211
230
  if (!TG_BOT_TOKEN || !TG_CHAT_ID) {
212
231
  return false;
213
232
  }
214
233
 
215
234
  let commentLink = '';
216
235
  const href = self.comment.match(/<a href="(.*?)">(.*?)<\/a>/g);
236
+
217
237
  if (href !== null) {
218
238
  for (var i = 0; i < href.length; i++) {
219
239
  href[i] =
@@ -261,16 +281,17 @@ module.exports = class extends think.Service {
261
281
  },
262
282
  };
263
283
 
264
- return request({
265
- uri: `https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage`,
284
+ const form = new FormData();
285
+
286
+ form.append('text', nunjucks.renderString(contentTG, data));
287
+ form.append('chat_id', TG_CHAT_ID);
288
+ form.append('parse_mode', 'MarkdownV2');
289
+
290
+ return fetch(`https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage`, {
266
291
  method: 'POST',
267
- form: {
268
- text: nunjucks.renderString(contentTG, data),
269
- chat_id: TG_CHAT_ID,
270
- parse_mode: 'MarkdownV2',
271
- },
272
- json: true,
273
- });
292
+ header: form.getHeaders(),
293
+ body: form,
294
+ }).then((resp) => resp.json());
274
295
  }
275
296
 
276
297
  async pushplus({ title, content }, self, parent) {
@@ -298,27 +319,30 @@ module.exports = class extends think.Service {
298
319
  postUrl: SITE_URL + self.url + '#' + self.objectId,
299
320
  },
300
321
  };
322
+
301
323
  title = nunjucks.renderString(title, data);
302
324
  content = nunjucks.renderString(content, data);
303
325
 
304
- return request({
305
- uri: `http://www.pushplus.plus/send/${PUSH_PLUS_KEY}`,
326
+ const form = new FormData();
327
+
328
+ form.append('topic', topic);
329
+ form.append('template', template);
330
+ form.append('channel', channel);
331
+ form.append('webhook', webhook);
332
+ form.append('callbackUrl', callbackUrl);
333
+ form.append('title', title);
334
+ form.append('content', content);
335
+
336
+ return fetch(`http://www.pushplus.plus/send/${PUSH_PLUS_KEY}`, {
306
337
  method: 'POST',
307
- form: {
308
- title,
309
- content,
310
- topic,
311
- template,
312
- channel,
313
- webhook,
314
- callbackUrl,
315
- },
316
- json: true,
317
- });
338
+ header: form.getHeaders(),
339
+ body: form,
340
+ }).then((resp) => resp.json());
318
341
  }
319
342
 
320
343
  async discord({ title, content }, self, parent) {
321
344
  const { DISCORD_WEBHOOK, SITE_NAME, SITE_URL } = process.env;
345
+
322
346
  if (!DISCORD_WEBHOOK) {
323
347
  return false;
324
348
  }
@@ -332,6 +356,7 @@ module.exports = class extends think.Service {
332
356
  postUrl: SITE_URL + self.url + '#' + self.objectId,
333
357
  },
334
358
  };
359
+
335
360
  title = nunjucks.renderString(title, data);
336
361
  content = nunjucks.renderString(
337
362
  think.config('DiscordTemplate') ||
@@ -343,14 +368,15 @@ module.exports = class extends think.Service {
343
368
  data
344
369
  );
345
370
 
346
- return request({
347
- uri: DISCORD_WEBHOOK,
371
+ const form = new FormData();
372
+
373
+ form.append('content', `${title}\n${content}`);
374
+
375
+ return fetch(DISCORD_WEBHOOK, {
348
376
  method: 'POST',
349
- form: {
350
- content: title + '\n' + content,
351
- },
352
- json: true,
353
- });
377
+ header: form.getHeaders(),
378
+ body: form,
379
+ }).then((resp) => resp.json());
354
380
  }
355
381
 
356
382
  async run(comment, parent, disableAuthorNotify = false) {
@@ -394,6 +420,7 @@ module.exports = class extends think.Service {
394
420
  const telegram = await this.telegram(comment, parent);
395
421
  const pushplus = await this.pushplus({ title, content }, comment, parent);
396
422
  const discord = await this.discord({ title, content }, comment, parent);
423
+
397
424
  if (
398
425
  [wechat, qq, telegram, qywxAmWechat, pushplus, discord].every(
399
426
  think.isEmpty
@@ -408,6 +435,7 @@ module.exports = class extends think.Service {
408
435
  (social) => 'mail.' + social
409
436
  );
410
437
  const fakeMail = new RegExp(`@(${disallowList.join('|')})$`, 'i');
438
+
411
439
  if (parent && !fakeMail.test(parent.mail) && comment.status !== 'waiting') {
412
440
  mailList.push({
413
441
  to: parent.mail,
@@ -436,6 +464,7 @@ module.exports = class extends think.Service {
436
464
  for (let i = 0; i < mailList.length; i++) {
437
465
  try {
438
466
  const response = await this.mail(mailList[i], comment, parent);
467
+
439
468
  console.log('Notification mail send success: %s', response);
440
469
  } catch (e) {
441
470
  console.log('Mail send fail:', e);