@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 +2 -2
- package/src/config/config.js +6 -0
- package/src/config/extend.js +14 -6
- package/src/controller/comment.js +56 -8
- package/src/controller/db.js +1 -1
- package/src/controller/token/2fa.js +1 -1
- package/src/controller/user/password.js +7 -2
- package/src/controller/user.js +32 -16
- package/src/controller/verification.js +3 -3
- package/src/extend/controller.js +11 -0
- package/src/extend/think.js +11 -0
- package/src/locales/en.json +15 -0
- package/src/locales/index.js +12 -0
- package/src/locales/zh-CN.json +15 -0
- package/src/locales/zh-TW.json +15 -0
- package/src/service/storage/cloudbase.js +22 -7
- package/src/service/storage/deta.js +39 -8
- package/src/service/storage/github.js +17 -2
- package/src/service/storage/leancloud.js +40 -5
- package/src/service/storage/mongodb.js +9 -2
- package/src/service/storage/mysql.js +8 -2
- package/src/service/storage/postgresql.js +7 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/vercel",
|
|
3
|
-
"version": "1.
|
|
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
|
|
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"
|
package/src/config/config.js
CHANGED
|
@@ -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,
|
package/src/config/extend.js
CHANGED
|
@@ -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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.ctx.status = 500;
|
|
19
|
+
async webhook(type, data) {
|
|
20
|
+
const { WEBHOOK } = process.env;
|
|
21
|
+
if (!WEBHOOK) {
|
|
22
|
+
return;
|
|
23
23
|
}
|
|
24
|
-
|
|
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
|
|
464
|
-
|
|
507
|
+
let parentComment;
|
|
465
508
|
if (pid) {
|
|
466
|
-
|
|
467
|
-
|
|
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 },
|
|
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,
|
|
525
|
+
await this.hook('postSave', resp, parentComment);
|
|
478
526
|
|
|
479
527
|
think.logger.debug(`Comment post hooks postSave done!`);
|
|
480
528
|
|
package/src/controller/db.js
CHANGED
|
@@ -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++) {
|
|
@@ -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:
|
|
40
|
-
|
|
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();
|
package/src/controller/user.js
CHANGED
|
@@ -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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
};
|
package/src/extend/controller.js
CHANGED
|
@@ -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
|
};
|
package/src/extend/think.js
CHANGED
|
@@ -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,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
|
|
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
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
|
196
|
-
|
|
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 = {},
|
|
290
|
+
async count(where = {}, { group } = {}) {
|
|
291
291
|
const instance = await this.collection(this.tableName);
|
|
292
292
|
const data = this.where(instance, where);
|
|
293
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|