@waline/vercel 1.13.6 → 1.15.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.13.6",
3
+ "version": "1.15.1",
4
4
  "description": "vercel server for waline comment system",
5
5
  "keywords": [
6
6
  "waline",
@@ -42,7 +42,7 @@
42
42
  "think-model-mysql": "^1.1.7",
43
43
  "think-model-postgresql": "1.1.6",
44
44
  "think-model-sqlite": "^1.2.3",
45
- "think-mongo": "^2.1.2",
45
+ "think-mongo": "^2.2.1",
46
46
  "think-router-rest": "^1.0.5",
47
47
  "thinkjs": "^3.2.14",
48
48
  "ua-parser-js": "^1.0.2"
@@ -37,6 +37,8 @@ const {
37
37
  TG_TEMPLATE,
38
38
  WX_TEMPLATE,
39
39
  DISCORD_TEMPLATE,
40
+
41
+ LEVELS,
40
42
  } = process.env;
41
43
 
42
44
  let storage = 'leancloud';
@@ -104,6 +106,10 @@ module.exports = {
104
106
  disallowIPList: [],
105
107
  secureDomains: SECURE_DOMAINS ? SECURE_DOMAINS.split(/\s*,\s*/) : undefined,
106
108
  disableUserAgent: DISABLE_USERAGENT && !isFalse(DISABLE_USERAGENT),
109
+ levels:
110
+ !LEVELS || isFalse(LEVELS)
111
+ ? false
112
+ : LEVELS.split(/\s*,\s*/).map((v) => Number(v)),
107
113
  avatarProxy,
108
114
  oauthUrl,
109
115
  markdown,
@@ -1,5 +1,6 @@
1
1
  const Model = require('think-model');
2
2
  const Mongo = require('think-mongo');
3
+ const request = require('request-promise-native');
3
4
 
4
5
  module.exports = [
5
6
  Model(think.app),
@@ -15,13 +16,20 @@ module.exports = [
15
16
  const { protocol, host } = this;
16
17
  return `${protocol}://${host}`;
17
18
  },
18
- },
19
- controller: {
20
- fail(...args) {
21
- if (this.ctx.status === 200) {
22
- this.ctx.status = 500;
19
+ async webhook(type, data) {
20
+ const { WEBHOOK } = process.env;
21
+ if (!WEBHOOK) {
22
+ return;
23
23
  }
24
- this.ctx.fail(...args);
24
+
25
+ return request({
26
+ uri: WEBHOOK,
27
+ method: 'POST',
28
+ headers: {
29
+ 'content-type': 'application/json',
30
+ },
31
+ body: JSON.stringify({ type, data }),
32
+ });
25
33
  },
26
34
  },
27
35
  },
@@ -305,6 +305,50 @@ module.exports = class extends BaseRest {
305
305
  );
306
306
  }
307
307
 
308
+ if (think.isArray(this.config('levels'))) {
309
+ const countWhere = {
310
+ status: ['NOT IN', ['waiting', 'spam']],
311
+ _complex: {},
312
+ };
313
+ if (user_ids.length) {
314
+ countWhere._complex.user_id = ['IN', user_ids];
315
+ }
316
+ const mails = Array.from(
317
+ new Set(comments.map(({ mail }) => mail).filter((v) => v))
318
+ );
319
+ if (mails.length) {
320
+ countWhere._complex.mail = ['IN', mails];
321
+ }
322
+ if (!think.isEmpty(countWhere._complex)) {
323
+ countWhere._complex._logic = 'or';
324
+ } else {
325
+ delete countWhere._complex;
326
+ }
327
+ const counts = await this.modelInstance.count(countWhere, {
328
+ group: ['user_id', 'mail'],
329
+ });
330
+ comments.forEach((cmt) => {
331
+ const countItem = (counts || []).find(({ mail, user_id }) => {
332
+ if (user_id) {
333
+ return user_id === cmt.user_id;
334
+ }
335
+ return mail === cmt.mail;
336
+ });
337
+
338
+ let level = 0;
339
+ if (countItem) {
340
+ const _level = think.findLastIndex(
341
+ this.config('levels'),
342
+ (l) => l <= countItem.count
343
+ );
344
+ if (_level !== -1) {
345
+ level = _level;
346
+ }
347
+ }
348
+ cmt.level = level;
349
+ });
350
+ }
351
+
308
352
  return this.json({
309
353
  page,
310
354
  totalPages: Math.ceil(rootCount / pageSize),
@@ -388,7 +432,7 @@ module.exports = class extends BaseRest {
388
432
  'The comment author had post same comment content before'
389
433
  );
390
434
 
391
- return this.fail('Duplicate Content');
435
+ return this.fail(this.locale('Duplicate Content'));
392
436
  }
393
437
 
394
438
  think.logger.debug('Comment duplicate check OK!');
@@ -403,7 +447,7 @@ module.exports = class extends BaseRest {
403
447
 
404
448
  if (!think.isEmpty(recent)) {
405
449
  think.logger.debug(`The author has posted in ${IPQPS} seconeds.`);
406
- return this.fail('Comment too fast!');
450
+ return this.fail(this.locale('Comment too fast!'));
407
451
  }
408
452
 
409
453
  think.logger.debug(`Comment post frequence check OK!`);
@@ -460,21 +504,25 @@ module.exports = class extends BaseRest {
460
504
 
461
505
  think.logger.debug(`Comment have been added to storage.`);
462
506
 
463
- let parrentComment;
464
-
507
+ let parentComment;
465
508
  if (pid) {
466
- parrentComment = await this.modelInstance.select({ objectId: pid });
467
- parrentComment = parrentComment[0];
509
+ parentComment = await this.modelInstance.select({ objectId: pid });
510
+ parentComment = parentComment[0];
468
511
  }
469
512
 
513
+ await this.ctx.webhook('new_comment', {
514
+ comment: { ...resp, rawComment: comment },
515
+ reply: parentComment,
516
+ });
517
+
470
518
  if (comment.status !== 'spam') {
471
519
  const notify = this.service('notify');
472
- await notify.run({ ...resp, rawComment: comment }, parrentComment);
520
+ await notify.run({ ...resp, rawComment: comment }, parentComment);
473
521
  }
474
522
 
475
523
  think.logger.debug(`Comment notify done!`);
476
524
 
477
- await this.hook('postSave', resp, parrentComment);
525
+ await this.hook('postSave', resp, parentComment);
478
526
 
479
527
  think.logger.debug(`Comment post hooks postSave done!`);
480
528
 
@@ -35,7 +35,7 @@ module.exports = class extends BaseRest {
35
35
  const jsonText = await readFileAsync(file.path, 'utf-8');
36
36
  const importData = JSON.parse(jsonText);
37
37
  if (!importData || importData.type !== 'waline') {
38
- return this.fail('import data format not support!');
38
+ return this.fail(this.locale('import data format not support!'));
39
39
  }
40
40
 
41
41
  for (let i = 0; i < importData.tables.length; i++) {
@@ -47,7 +47,7 @@ module.exports = class extends BaseRest {
47
47
  });
48
48
 
49
49
  if (!verified) {
50
- return this.fail('TWO_FACTOR_AUTH_ERROR_DETAIL');
50
+ return this.fail(this.locale('TWO_FACTOR_AUTH_ERROR_DETAIL'));
51
51
  }
52
52
 
53
53
  const userModel = this.service(
@@ -36,8 +36,13 @@ module.exports = class extends BaseRest {
36
36
  ? `"${SENDER_NAME}" <${SENDER_EMAIL}>`
37
37
  : SMTP_USER,
38
38
  to: user[0].email,
39
- subject: `【${SITE_NAME || 'Waline'}】Reset Password`,
40
- html: `Please click <a href="${profileUrl}">${profileUrl}</a> to login and change your password as soon as possible!`,
39
+ subject: this.locale('[{{name}}] Reset Password', {
40
+ name: SITE_NAME || 'Waline',
41
+ }),
42
+ html: this.locale(
43
+ 'Please click <a href="{{url}}">{{url}}</a> to login and change your password as soon as possible!',
44
+ { url: profileUrl }
45
+ ),
41
46
  });
42
47
 
43
48
  return this.success();
@@ -21,7 +21,7 @@ module.exports = class extends BaseRest {
21
21
  !think.isEmpty(resp) &&
22
22
  ['administrator', 'guest'].includes(resp[0].type)
23
23
  ) {
24
- return this.fail('USER_EXIST');
24
+ return this.fail(this.locale('USER_EXIST'));
25
25
  }
26
26
 
27
27
  const count = await this.modelInstance.count();
@@ -56,21 +56,37 @@ module.exports = class extends BaseRest {
56
56
  return this.success();
57
57
  }
58
58
 
59
- const notify = this.service('notify');
60
- const apiUrl =
61
- this.ctx.serverURL +
62
- '/verification?' +
63
- qs.stringify({ token, email: data.email });
64
-
65
- await notify.transporter.sendMail({
66
- from:
67
- SENDER_EMAIL && SENDER_NAME
68
- ? `"${SENDER_NAME}" <${SENDER_EMAIL}>`
69
- : SMTP_USER,
70
- to: data.email,
71
- subject: `【${SITE_NAME || 'Waline'}】注册确认邮件`,
72
- html: `请点击 ${apiUrl} 确认注册,链接有效时间为 1 个小时。如果不是你在注册,请忽略这封邮件。`,
73
- });
59
+ try {
60
+ const notify = this.service('notify');
61
+ const apiUrl =
62
+ this.ctx.serverURL +
63
+ '/verification?' +
64
+ qs.stringify({ token, email: data.email });
65
+
66
+ await notify.transporter.sendMail({
67
+ from:
68
+ SENDER_EMAIL && SENDER_NAME
69
+ ? `"${SENDER_NAME}" <${SENDER_EMAIL}>`
70
+ : SMTP_USER,
71
+ to: data.email,
72
+ subject: this.locale('[{{name}}] Registration Confirm Mail', {
73
+ name: SITE_NAME || 'Waline',
74
+ }),
75
+ html: this.locale(
76
+ 'Please click <a href="{{url}}">{{url}}<a/> to confirm registration, the link is valid for 1 hour. If you are not registering, please ignore this email.',
77
+ { url: apiUrl }
78
+ ),
79
+ });
80
+ } catch (e) {
81
+ console.log(e);
82
+
83
+ return this.fail(
84
+ this.locale(
85
+ 'Registeration confirm mail send failed, please {%- if isAdmin -%}check your mail configuration{%- else -%}check your email address and contact administrator{%- endif -%}.',
86
+ { isAdmin: think.isEmpty(count) }
87
+ )
88
+ );
89
+ }
74
90
 
75
91
  return this.success({ verify: true });
76
92
  }
@@ -13,13 +13,13 @@ module.exports = class extends BaseRest {
13
13
  const { token, email } = this.get();
14
14
  const users = await this.modelInstance.select({ email });
15
15
  if (think.isEmpty(users)) {
16
- return this.fail('USER_NOT_EXIST');
16
+ return this.fail(this.locale('USER_NOT_EXIST'));
17
17
  }
18
18
 
19
19
  const user = users[0];
20
20
  const match = user.type.match(/^verify:(\d{4}):(\d+)$/i);
21
21
  if (!match) {
22
- return this.fail('USER_REGISTED');
22
+ return this.fail(this.locale('USER_REGISTED'));
23
23
  }
24
24
 
25
25
  if (token === match[1] && Date.now() < parseInt(match[2])) {
@@ -27,6 +27,6 @@ module.exports = class extends BaseRest {
27
27
  return this.redirect('/ui/login');
28
28
  }
29
29
 
30
- return this.fail('TOKEN_EXPIRED');
30
+ return this.fail(this.locale('TOKEN_EXPIRED'));
31
31
  }
32
32
  };
@@ -1,3 +1,6 @@
1
+ const nunjucks = require('nunjucks');
2
+ const locales = require('../locales');
3
+
1
4
  module.exports = {
2
5
  success(...args) {
3
6
  this.ctx.success(...args);
@@ -7,4 +10,12 @@ module.exports = {
7
10
  this.ctx.fail(...args);
8
11
  return think.prevent();
9
12
  },
13
+ locale(message, variables) {
14
+ const { lang } = this.get();
15
+ const locale = locales[(lang || '').toLowerCase()];
16
+ if (locale && locale[message]) {
17
+ message = locale[message];
18
+ }
19
+ return nunjucks.renderString(message, variables);
20
+ },
10
21
  };
@@ -7,4 +7,15 @@ module.exports = {
7
7
  isPrevent(err) {
8
8
  return think.isError(err) && err.message === preventMessage;
9
9
  },
10
+ findLastIndex(arr, fn) {
11
+ for (let i = arr.length - 1; i >= 0; i--) {
12
+ const ret = fn(arr[i], i, arr);
13
+ if (!ret) {
14
+ continue;
15
+ }
16
+ return i;
17
+ }
18
+
19
+ return -1;
20
+ },
10
21
  };
@@ -0,0 +1,15 @@
1
+ {
2
+ "import data format not support!": "import data format not support!",
3
+ "USER_EXIST": "USER_EXIST",
4
+ "USER_NOT_EXIST": "USER_NOT_EXIST",
5
+ "USER_REGISTED": "USER_REGISTED",
6
+ "TOKEN_EXPIRED": "TOKEN_EXPIRED",
7
+ "TWO_FACTOR_AUTH_ERROR_DETAIL": "TWO_FACTOR_AUTH_ERROR_DETAIL",
8
+ "[{{name}}] Registration Confirm Mail": "[{{name}}] Registration Confirm Mail",
9
+ "Please click <a href=\"{{url}}\">{{url}}<a/> to confirm registration, the link is valid for 1 hour. If you are not registering, please ignore this email.": "Please click <a href=\"{{url}}\">{{url}}<a/> to confirm registration, the link is valid for 1 hour. If you are not registering, please ignore this email.",
10
+ "[{{name}}] Reset Password": "[{{name}}] Reset Password",
11
+ "Please click <a href=\"{{url}}\">{{url}}</a> to login and change your password as soon as possible!": "Please click <a href=\"{{url}}\">{{url}}</a> to login and change your password as soon as possible!",
12
+ "Duplicate Content": "Duplicate Content",
13
+ "Comment too fast": "Comment too fast",
14
+ "Registeration confirm mail send failed, please {%- if isAdmin -%}check your mail configuration{%- else -%}check your email address and contact administrator{%- endif -%}.": "Registeration confirm mail send failed, please {%- if isAdmin -%}check your mail configuration{%- else -%}check your email address and contact administrator{%- endif -%}."
15
+ }
@@ -0,0 +1,12 @@
1
+ const en = require('./en.json');
2
+ const zhCN = require('./zh-CN.json');
3
+ const zhTW = require('./zh-TW.json');
4
+
5
+ module.exports = {
6
+ 'zh-cn': zhCN,
7
+ 'zh-tw': zhTW,
8
+ en: en,
9
+ 'en-us': en,
10
+ jp: en,
11
+ 'jp-jp': en,
12
+ };
@@ -0,0 +1,15 @@
1
+ {
2
+ "import data format not support!": "文件格式不支持",
3
+ "USER_EXIST": "用户已存在",
4
+ "USER_NOT_EXIST": "用户不存在",
5
+ "USER_REGISTED": "用户已注册",
6
+ "TOKEN_EXPIRED": "密钥已过期",
7
+ "TWO_FACTOR_AUTH_ERROR_DETAIL": "二步验证失败",
8
+ "[{{name}}] Registration Confirm Mail": "【{{name}}】注册确认邮件",
9
+ "Please click <a href=\"{{url}}\">{{url}}<a/> to confirm registration, the link is valid for 1 hour. If you are not registering, please ignore this email.": "请点击 <a href=\"{{url}}\">{{url}}</a> 确认注册,链接有效时间为 1 个小时。如果不是你在注册,请忽略这封邮件。",
10
+ "[{{name}}] Reset Password": "【{{name}}】重置密码",
11
+ "Please click <a href=\"{{url}}\">{{url}}</a> to login and change your password as soon as possible!": "请尽快点击链接 <a href=\"{{url}}\">{{url}}</a> 登录并修改你的密码!",
12
+ "Duplicate Content": "发送的内容之前已经发过",
13
+ "Comment too fast": "评论太快啦,请慢点!",
14
+ "Registeration confirm mail send failed, please {%- if isAdmin -%}check your mail configuration{%- else -%}check your email address and contact administrator{%- endif -%}.": "注册确认邮件发送失败,请{%- if isAdmin -%}检查一下网站的邮件相关配置{% else %}确认你的邮箱输入无误并联系管理员{%- endif -%}。"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "import data format not support!": "文件格式不支持",
3
+ "USER_EXIST": "用戶已存在",
4
+ "USER_NOT_EXIST": "用戶不存在",
5
+ "USER_REGISTED": "用戶已註冊",
6
+ "TOKEN_EXPIRED": "密鑰已過期",
7
+ "TWO_FACTOR_AUTH_ERROR_DETAIL": "二步驗證失敗",
8
+ "[{{name}}] Registration Confirm Mail": "『{{name}}』註冊確認郵件",
9
+ "Please click <a href=\"{{url}}\">{{url}}<a/> to confirm registration, the link is valid for 1 hour. If you are not registering, please ignore this email.": "請點擊 <a href=\"{{url}}\">{{url}}</a> 確認註冊,鏈接有效時間為 1 個小時。如果不是你在註冊,請忽略這封郵件。",
10
+ "[{{name}}] Reset Password": "『{{name}}』重置密碼",
11
+ "Please click <a href=\"{{url}}\">{{url}}</a> to login and change your password as soon as possible!": "請盡快點擊鏈接 <a href=\"{{url}}\">{{url}}</a> 登錄並修改你的密碼!",
12
+ "Duplicate Content": "發送的內容之前已經發過",
13
+ "Comment too fast": "評論太快啦,請慢點!",
14
+ "Registeration confirm mail send failed, please {%- if isAdmin -%}check your mail configuration{%- else -%}check your email address and contact administrator{%- endif -%}.": "註冊確認郵件發送失敗,{%- if isAdmin -%}檢查一下網站的郵件相關配置{% else %}確認你的郵箱輸入無誤後聯繫管理員{%- endif -%}。"
15
+ }
@@ -10,6 +10,7 @@ const app = cloudbase.init({
10
10
 
11
11
  const db = app.database();
12
12
  const _ = db.command;
13
+ const $ = db.command.aggregate;
13
14
  const collections = {};
14
15
 
15
16
  module.exports = class extends Base {
@@ -90,10 +91,10 @@ module.exports = class extends Base {
90
91
  return filter;
91
92
  }
92
93
 
93
- where(instance, where) {
94
+ where(instance, where, method = 'where') {
94
95
  const filter = this.parseWhere(where);
95
96
  if (!where._complex) {
96
- return instance.where(filter);
97
+ return instance[method](filter);
97
98
  }
98
99
 
99
100
  const filters = [];
@@ -106,7 +107,7 @@ module.exports = class extends Base {
106
107
  ...filter,
107
108
  });
108
109
  }
109
- return instance.where(_[where._complex._logic](...filters));
110
+ return instance[method](_[where._complex._logic](...filters));
110
111
  }
111
112
 
112
113
  async _select(where, { desc, limit, offset, field } = {}) {
@@ -147,10 +148,24 @@ module.exports = class extends Base {
147
148
  return data;
148
149
  }
149
150
 
150
- async count(where = {}) {
151
- const instance = await this.collection(this.tableName);
152
- const { total } = await this.where(instance, where).count();
153
- return total;
151
+ async count(where = {}, { group } = {}) {
152
+ let instance = await this.collection(this.tableName);
153
+ if (!group) {
154
+ instance = this.where(instance, where);
155
+ const { total } = await instance.count();
156
+ return total;
157
+ }
158
+
159
+ const _id = {};
160
+ group.forEach((f) => {
161
+ _id[f] = `$${f}`;
162
+ });
163
+ instance = instance.aggregate();
164
+ this.where(instance, where, 'match');
165
+ instance = instance.group({ _id, count: $.sum(1) });
166
+ const { data } = await instance.end();
167
+
168
+ return data.map(({ _id, count }) => ({ ..._id, count }));
154
169
  }
155
170
 
156
171
  async add(data) {
@@ -184,16 +184,47 @@ module.exports = class extends Base {
184
184
  return data;
185
185
  }
186
186
 
187
- async count(where = {}) {
188
- const conditions = this.where(where);
189
- if (think.isArray(conditions)) {
190
- return Promise.all(
191
- conditions.map((condition) => this.count(condition))
192
- ).then((counts) => counts.reduce((a, b) => a + b, 0));
187
+ async count(where = {}, { group } = {}) {
188
+ if (!group) {
189
+ const conditions = this.where(where);
190
+ if (think.isArray(conditions)) {
191
+ return Promise.all(
192
+ conditions.map((condition) => this.count(condition))
193
+ ).then((counts) => counts.reduce((a, b) => a + b, 0));
194
+ }
195
+
196
+ const { count } = await this.instance.fetch(conditions);
197
+ return count;
193
198
  }
194
199
 
195
- const { count } = await this.instance.fetch(conditions);
196
- return count;
200
+ const counts = [];
201
+ for (let i = 0; i < group.length; i++) {
202
+ const groupName = group[i];
203
+ if (!where._complex || !Array.isArray(where._complex[groupName])) {
204
+ continue;
205
+ }
206
+
207
+ const groupFlatValue = {};
208
+ group.slice(0, i).forEach((group) => {
209
+ groupFlatValue[group] = null;
210
+ });
211
+
212
+ for (let j = 0; j < where._complex[groupName][1].length; j++) {
213
+ const groupWhere = {
214
+ ...where,
215
+ ...groupFlatValue,
216
+ _complex: undefined,
217
+ [groupName]: where._complex[groupName][1][j],
218
+ };
219
+ const num = await this.count(groupWhere);
220
+ counts.push({
221
+ ...groupFlatValue,
222
+ [groupName]: where._complex[groupName][1][j],
223
+ count: num,
224
+ });
225
+ }
226
+ }
227
+ return counts;
197
228
  }
198
229
 
199
230
  async add(data) {
@@ -287,10 +287,25 @@ module.exports = class extends Base {
287
287
  }
288
288
 
289
289
  // eslint-disable-next-line no-unused-vars
290
- async count(where = {}, options = {}) {
290
+ async count(where = {}, { group } = {}) {
291
291
  const instance = await this.collection(this.tableName);
292
292
  const data = this.where(instance, where);
293
- return data.length;
293
+ if (!group) {
294
+ return data.length;
295
+ }
296
+
297
+ const counts = {};
298
+ for (let i = 0; i < data.length; i++) {
299
+ const key = group.map((field) => data[field]).join();
300
+ if (!counts[key]) {
301
+ counts[key] = { count: 0 };
302
+ group.forEach((field) => {
303
+ counts[key][field] = data[field];
304
+ });
305
+ }
306
+ counts[key].count += 1;
307
+ }
308
+ return Object.keys(counts);
294
309
  }
295
310
 
296
311
  async add(
@@ -128,12 +128,47 @@ module.exports = class extends Base {
128
128
 
129
129
  async count(where = {}, options = {}) {
130
130
  const instance = this.where(this.tableName, where);
131
- return instance.count(options).catch((e) => {
132
- if (e.code === 101) {
133
- return 0;
131
+ if (!options.group) {
132
+ return instance.count(options).catch((e) => {
133
+ if (e.code === 101) {
134
+ return 0;
135
+ }
136
+ throw e;
137
+ });
138
+ }
139
+
140
+ // todo: query optimize
141
+ const counts = [];
142
+ for (let i = 0; i < options.group.length; i++) {
143
+ const groupName = options.group[i];
144
+ if (!where._complex || !Array.isArray(where._complex[groupName])) {
145
+ continue;
134
146
  }
135
- throw e;
136
- });
147
+
148
+ const groupFlatValue = {};
149
+ options.group.slice(0, i).forEach((group) => {
150
+ groupFlatValue[group] = null;
151
+ });
152
+
153
+ for (let j = 0; j < where._complex[groupName][1].length; j++) {
154
+ const groupWhere = {
155
+ ...where,
156
+ ...groupFlatValue,
157
+ _complex: undefined,
158
+ [groupName]: where._complex[groupName][1][j],
159
+ };
160
+ const num = await this.count(groupWhere, {
161
+ ...options,
162
+ group: undefined,
163
+ });
164
+ counts.push({
165
+ ...groupFlatValue,
166
+ [groupName]: where._complex[groupName][1][j],
167
+ count: num,
168
+ });
169
+ }
170
+ }
171
+ return counts;
137
172
  }
138
173
 
139
174
  async add(
@@ -114,10 +114,17 @@ module.exports = class extends Base {
114
114
  }));
115
115
  }
116
116
 
117
- async count(where = {}) {
117
+ async count(where = {}, { group } = {}) {
118
118
  const instance = this.mongo(this.tableName);
119
119
  this.where(instance, where);
120
- return instance.count();
120
+ if (group) {
121
+ instance.group(group);
122
+ }
123
+ const data = await instance.count({ raw: group });
124
+ if (!Array.isArray(data)) {
125
+ return data;
126
+ }
127
+ return data.map(({ _id, total: count }) => ({ ..._id, count }));
121
128
  }
122
129
 
123
130
  async add(data) {
@@ -50,10 +50,16 @@ module.exports = class extends Base {
50
50
  return data.map(({ id, ...cmt }) => ({ ...cmt, objectId: id }));
51
51
  }
52
52
 
53
- async count(where = {}) {
53
+ async count(where = {}, { group } = {}) {
54
54
  const instance = this.model(this.tableName);
55
55
  instance.where(this.parseWhere(where));
56
- return instance.count();
56
+ if (!group) {
57
+ return instance.count();
58
+ }
59
+
60
+ instance.field([...group, 'COUNT(*) as count']);
61
+ instance.group(group);
62
+ return instance.select();
57
63
  }
58
64
 
59
65
  async add(data) {
@@ -26,7 +26,13 @@ module.exports = class extends MySQL {
26
26
  async count(...args) {
27
27
  let result = await super.count(...args);
28
28
  try {
29
- result = parseInt(result);
29
+ if (Array.isArray(result)) {
30
+ result.forEach((r) => {
31
+ r.count = parseInt(r.count);
32
+ });
33
+ } else {
34
+ result = parseInt(result);
35
+ }
30
36
  } catch (e) {
31
37
  console.log(e);
32
38
  }