@waline/vercel 1.6.1 → 1.8.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/README.md +3 -3
- package/package.json +14 -14
- package/src/config/adapter.js +3 -4
- package/src/controller/comment.js +103 -61
- package/src/controller/index.js +3 -2
- package/src/logic/base.js +1 -0
- package/src/service/avatar.js +10 -2
- package/src/service/markdown/xss.js +10 -4
- package/src/service/notify.js +47 -4
- package/src/service/storage/cloudbase.js +25 -3
- package/src/service/storage/github.js +34 -3
- package/src/service/storage/inspirecloud.js +43 -16
- package/src/service/storage/leancloud.js +35 -10
- package/src/service/storage/mongodb.js +52 -45
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# @waline/vercel
|
|
2
2
|
|
|
3
|
-

|
|
3
|
+

|
|
4
4
|
|
|
5
5
|
This is the backend for Waline comment system.
|
|
6
6
|
|
|
@@ -20,6 +20,6 @@ We support [Akismet](https://akismet.com/) spam protection service default. If y
|
|
|
20
20
|
|
|
21
21
|
## Deploy
|
|
22
22
|
|
|
23
|
-
[
|
|
23
|
+
[](https://vercel.com/import/project?template=https://github.com/walinejs/waline/tree/main/example)
|
|
24
24
|
|
|
25
25
|
Click it to deploy quickly!
|
package/package.json
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/vercel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "vercel server for waline comment system",
|
|
5
5
|
"repository": "https://github.com/walinejs/waline",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "lizheming <i@imnerd.org>",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@byteinspire/api": "^1.0.12",
|
|
10
|
-
"@cloudbase/node-sdk": "^2.
|
|
11
|
-
"@koa/cors": "^3.
|
|
10
|
+
"@cloudbase/node-sdk": "^2.9.0",
|
|
11
|
+
"@koa/cors": "^3.2.0",
|
|
12
12
|
"akismet": "^2.0.6",
|
|
13
|
-
"deta": "^1.0
|
|
14
|
-
"dompurify": "^2.3.
|
|
13
|
+
"deta": "^1.1.0",
|
|
14
|
+
"dompurify": "^2.3.6",
|
|
15
15
|
"fast-csv": "^4.3.6",
|
|
16
|
-
"jsdom": "^
|
|
16
|
+
"jsdom": "^19.0.0",
|
|
17
17
|
"jsonwebtoken": "^8.5.1",
|
|
18
|
-
"katex": "^0.
|
|
19
|
-
"leancloud-storage": "^4.12.
|
|
20
|
-
"markdown-it": "^12.2
|
|
18
|
+
"katex": "^0.15.3",
|
|
19
|
+
"leancloud-storage": "^4.12.2",
|
|
20
|
+
"markdown-it": "^12.3.2",
|
|
21
21
|
"markdown-it-emoji": "^2.0.0",
|
|
22
22
|
"markdown-it-sub": "^1.0.0",
|
|
23
23
|
"markdown-it-sup": "^1.0.0",
|
|
24
24
|
"mathjax-full": "^3.2.0",
|
|
25
|
-
"nodemailer": "^6.7.
|
|
25
|
+
"nodemailer": "^6.7.3",
|
|
26
26
|
"nunjucks": "^3.2.3",
|
|
27
27
|
"phpass": "^0.1.1",
|
|
28
|
-
"prismjs": "^1.
|
|
28
|
+
"prismjs": "^1.27.0",
|
|
29
29
|
"request": "^2.88.2",
|
|
30
30
|
"request-promise-native": "^1.0.9",
|
|
31
|
-
"think-logger3": "^1.
|
|
31
|
+
"think-logger3": "^1.3.1",
|
|
32
32
|
"think-model": "^1.5.4",
|
|
33
|
-
"think-model-mysql": "^1.1.
|
|
33
|
+
"think-model-mysql": "^1.1.7",
|
|
34
34
|
"think-model-postgresql": "^1.1.6",
|
|
35
35
|
"think-model-sqlite": "^1.2.2",
|
|
36
36
|
"think-mongo": "^2.1.2",
|
|
37
37
|
"think-router-rest": "^1.0.5",
|
|
38
38
|
"thinkjs": "^3.2.14",
|
|
39
|
-
"ua-parser-js": "^0.
|
|
39
|
+
"ua-parser-js": "^1.0.2"
|
|
40
40
|
}
|
|
41
41
|
}
|
package/src/config/adapter.js
CHANGED
|
@@ -37,10 +37,9 @@ const {
|
|
|
37
37
|
} = process.env;
|
|
38
38
|
|
|
39
39
|
let type = 'common';
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
};
|
|
40
|
+
const mongoOpt = {};
|
|
41
|
+
if (MONGO_REPLICASET) mongoOpt.replicaSet = MONGO_REPLICASET;
|
|
42
|
+
if (MONGO_AUTHSOURCE) mongoOpt.authSource = MONGO_AUTHSOURCE;
|
|
44
43
|
|
|
45
44
|
if (MONGO_DB) {
|
|
46
45
|
type = 'mongo';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const helper = require('think-helper');
|
|
2
1
|
const parser = require('ua-parser-js');
|
|
3
2
|
const BaseRest = require('./rest');
|
|
4
3
|
const akismet = require('../service/akismet');
|
|
@@ -8,7 +7,8 @@ const markdownParser = getMarkdownParser();
|
|
|
8
7
|
async function formatCmt(
|
|
9
8
|
{ ua, user_id, ...comment },
|
|
10
9
|
users = [],
|
|
11
|
-
{ avatarProxy }
|
|
10
|
+
{ avatarProxy },
|
|
11
|
+
loginUser
|
|
12
12
|
) {
|
|
13
13
|
ua = parser(ua);
|
|
14
14
|
if (!think.config('disableUserAgent')) {
|
|
@@ -36,9 +36,12 @@ async function formatCmt(
|
|
|
36
36
|
? avatarProxy + '?url=' + encodeURIComponent(avatarUrl)
|
|
37
37
|
: avatarUrl;
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const isAdmin = loginUser && loginUser.type === 'administrator';
|
|
40
|
+
if (!isAdmin) {
|
|
41
|
+
delete comment.mail;
|
|
42
|
+
} else {
|
|
43
|
+
comment.orig = comment.comment;
|
|
44
|
+
}
|
|
42
45
|
|
|
43
46
|
comment.comment = markdownParser(comment.comment);
|
|
44
47
|
return comment;
|
|
@@ -55,30 +58,40 @@ module.exports = class extends BaseRest {
|
|
|
55
58
|
|
|
56
59
|
async getAction() {
|
|
57
60
|
const { type } = this.get();
|
|
61
|
+
const { userInfo } = this.ctx.state;
|
|
58
62
|
|
|
59
63
|
switch (type) {
|
|
60
64
|
case 'recent': {
|
|
61
65
|
const { count } = this.get();
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
66
|
+
const where = {};
|
|
67
|
+
if (think.isEmpty(userInfo) || this.config('storage') === 'deta') {
|
|
68
|
+
where.status = ['NOT IN', ['waiting', 'spam']];
|
|
69
|
+
} else {
|
|
70
|
+
where._complex = {
|
|
71
|
+
_logic: 'or',
|
|
72
|
+
status: ['NOT IN', ['waiting', 'spam']],
|
|
73
|
+
user_id: userInfo.objectId,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const comments = await this.modelInstance.select(where, {
|
|
78
|
+
desc: 'insertedAt',
|
|
79
|
+
limit: count,
|
|
80
|
+
field: [
|
|
81
|
+
'status',
|
|
82
|
+
'comment',
|
|
83
|
+
'insertedAt',
|
|
84
|
+
'link',
|
|
85
|
+
'mail',
|
|
86
|
+
'nick',
|
|
87
|
+
'url',
|
|
88
|
+
'pid',
|
|
89
|
+
'rid',
|
|
90
|
+
'ua',
|
|
91
|
+
'user_id',
|
|
92
|
+
'sticky',
|
|
93
|
+
],
|
|
94
|
+
});
|
|
82
95
|
|
|
83
96
|
const userModel = this.service(
|
|
84
97
|
`storage/${this.config('storage')}`,
|
|
@@ -100,20 +113,26 @@ module.exports = class extends BaseRest {
|
|
|
100
113
|
|
|
101
114
|
return this.json(
|
|
102
115
|
await Promise.all(
|
|
103
|
-
comments.map((cmt) =>
|
|
116
|
+
comments.map((cmt) =>
|
|
117
|
+
formatCmt(cmt, users, this.config(), userInfo)
|
|
118
|
+
)
|
|
104
119
|
)
|
|
105
120
|
);
|
|
106
121
|
}
|
|
107
122
|
|
|
108
123
|
case 'count': {
|
|
109
124
|
const { url } = this.get();
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
125
|
+
const where = { url: ['IN', url] };
|
|
126
|
+
if (think.isEmpty(userInfo) || this.config('storage') === 'deta') {
|
|
127
|
+
where.status = ['NOT IN', ['waiting', 'spam']];
|
|
128
|
+
} else {
|
|
129
|
+
where._complex = {
|
|
130
|
+
_logic: 'or',
|
|
113
131
|
status: ['NOT IN', ['waiting', 'spam']],
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
132
|
+
user_id: userInfo.objectId,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const data = await this.modelInstance.select(where, { field: ['url'] });
|
|
117
136
|
const counts = url.map(
|
|
118
137
|
(u) => data.filter(({ url }) => url === u).length
|
|
119
138
|
);
|
|
@@ -179,35 +198,42 @@ module.exports = class extends BaseRest {
|
|
|
179
198
|
spamCount,
|
|
180
199
|
waitingCount,
|
|
181
200
|
data: await Promise.all(
|
|
182
|
-
comments.map((cmt) =>
|
|
201
|
+
comments.map((cmt) =>
|
|
202
|
+
formatCmt(cmt, users, this.config(), userInfo)
|
|
203
|
+
)
|
|
183
204
|
),
|
|
184
205
|
});
|
|
185
206
|
}
|
|
186
207
|
|
|
187
208
|
default: {
|
|
188
209
|
const { path: url, page, pageSize } = this.get();
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
210
|
+
const where = { url };
|
|
211
|
+
if (think.isEmpty(userInfo) || this.config('storage') === 'deta') {
|
|
212
|
+
where.status = ['NOT IN', ['waiting', 'spam']];
|
|
213
|
+
} else {
|
|
214
|
+
where._complex = {
|
|
215
|
+
_logic: 'or',
|
|
193
216
|
status: ['NOT IN', ['waiting', 'spam']],
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
217
|
+
user_id: userInfo.objectId,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const comments = await this.modelInstance.select(where, {
|
|
222
|
+
desc: 'insertedAt',
|
|
223
|
+
field: [
|
|
224
|
+
'status',
|
|
225
|
+
'comment',
|
|
226
|
+
'insertedAt',
|
|
227
|
+
'link',
|
|
228
|
+
'mail',
|
|
229
|
+
'nick',
|
|
230
|
+
'pid',
|
|
231
|
+
'rid',
|
|
232
|
+
'ua',
|
|
233
|
+
'user_id',
|
|
234
|
+
'sticky',
|
|
235
|
+
],
|
|
236
|
+
});
|
|
211
237
|
|
|
212
238
|
const userModel = this.service(
|
|
213
239
|
`storage/${this.config('storage')}`,
|
|
@@ -241,11 +267,16 @@ module.exports = class extends BaseRest {
|
|
|
241
267
|
count: comments.length,
|
|
242
268
|
data: await Promise.all(
|
|
243
269
|
rootComments.map(async (comment) => {
|
|
244
|
-
const cmt = await formatCmt(
|
|
270
|
+
const cmt = await formatCmt(
|
|
271
|
+
comment,
|
|
272
|
+
users,
|
|
273
|
+
this.config(),
|
|
274
|
+
userInfo
|
|
275
|
+
);
|
|
245
276
|
cmt.children = await Promise.all(
|
|
246
277
|
comments
|
|
247
278
|
.filter(({ rid }) => rid === cmt.objectId)
|
|
248
|
-
.map((cmt) => formatCmt(cmt, users, this.config()))
|
|
279
|
+
.map((cmt) => formatCmt(cmt, users, this.config(), userInfo))
|
|
249
280
|
.reverse()
|
|
250
281
|
);
|
|
251
282
|
return cmt;
|
|
@@ -403,12 +434,19 @@ module.exports = class extends BaseRest {
|
|
|
403
434
|
|
|
404
435
|
think.logger.debug(`Comment post hooks postSave done!`);
|
|
405
436
|
|
|
406
|
-
return this.success(
|
|
437
|
+
return this.success(
|
|
438
|
+
await formatCmt(resp, [userInfo], this.config(), userInfo)
|
|
439
|
+
);
|
|
407
440
|
}
|
|
408
441
|
|
|
409
442
|
async putAction() {
|
|
410
443
|
const data = this.post();
|
|
411
|
-
|
|
444
|
+
let oldData = await this.modelInstance.select({ objectId: this.id });
|
|
445
|
+
if (think.isEmpty(oldData)) {
|
|
446
|
+
return this.success();
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
oldData = oldData[0];
|
|
412
450
|
const preUpdateResp = await this.hook('preUpdate', {
|
|
413
451
|
...data,
|
|
414
452
|
objectId: this.id,
|
|
@@ -418,18 +456,22 @@ module.exports = class extends BaseRest {
|
|
|
418
456
|
return this.fail(preUpdateResp);
|
|
419
457
|
}
|
|
420
458
|
|
|
421
|
-
await this.modelInstance.update(data, {
|
|
459
|
+
const newData = await this.modelInstance.update(data, {
|
|
460
|
+
objectId: this.id,
|
|
461
|
+
});
|
|
422
462
|
|
|
423
463
|
if (
|
|
424
464
|
oldData.status === 'waiting' &&
|
|
425
465
|
data.status === 'approved' &&
|
|
426
466
|
oldData.pid
|
|
427
467
|
) {
|
|
428
|
-
let pComment = await this.modelInstance.select({
|
|
468
|
+
let pComment = await this.modelInstance.select({
|
|
469
|
+
objectId: oldData.pid,
|
|
470
|
+
});
|
|
429
471
|
pComment = pComment[0];
|
|
430
472
|
|
|
431
473
|
const notify = this.service('notify');
|
|
432
|
-
await notify.run(
|
|
474
|
+
await notify.run(newData, pComment, true);
|
|
433
475
|
}
|
|
434
476
|
|
|
435
477
|
await this.hook('postUpdate', data);
|
package/src/controller/index.js
CHANGED
|
@@ -19,10 +19,11 @@ module.exports = class extends think.Controller {
|
|
|
19
19
|
'color: white; background: #0078E7; padding:5px 0;',
|
|
20
20
|
'padding:4px;border:1px solid #0078E7;'
|
|
21
21
|
);
|
|
22
|
+
const params = new URLSearchParams(location.search.slice(1));
|
|
22
23
|
const waline = new Waline({
|
|
23
24
|
el: '#waline',
|
|
24
|
-
path: '/',
|
|
25
|
-
lang:
|
|
25
|
+
path: params.get('path') || '/',
|
|
26
|
+
lang: params.get('lng'),
|
|
26
27
|
serverURL: location.protocol + '//' + location.host + location.pathname.replace(/\\/+$/, '')
|
|
27
28
|
});
|
|
28
29
|
</script>
|
package/src/logic/base.js
CHANGED
package/src/service/avatar.js
CHANGED
|
@@ -5,6 +5,15 @@ const { GRAVATAR_STR } = process.env;
|
|
|
5
5
|
const env = new nunjucks.Environment();
|
|
6
6
|
env.addFilter('md5', (str) => helper.md5(str));
|
|
7
7
|
|
|
8
|
+
const DEFAULT_GRAVATAR_STR = `{%- set numExp = r/^[0-9]+$/g -%}
|
|
9
|
+
{%- set qqMailExp = r/^[0-9]+@qq.com$/ig -%}
|
|
10
|
+
{%- if numExp.test(nick) -%}
|
|
11
|
+
https://q1.qlogo.cn/g?b=qq&nk={{nick}}&s=100
|
|
12
|
+
{%- elif qqMailExp.test(mail) -%}
|
|
13
|
+
https://q1.qlogo.cn/g?b=qq&nk={{mail|replace('@qq.com', '')}}&s=100
|
|
14
|
+
{%- else -%}
|
|
15
|
+
https://seccdn.libravatar.org/avatar/{{mail|md5}}
|
|
16
|
+
{%- endif -%}`;
|
|
8
17
|
module.exports = class extends think.Service {
|
|
9
18
|
async stringify(comment) {
|
|
10
19
|
const fn = think.config('avatarUrl');
|
|
@@ -15,8 +24,7 @@ module.exports = class extends think.Service {
|
|
|
15
24
|
}
|
|
16
25
|
}
|
|
17
26
|
|
|
18
|
-
const gravatarStr =
|
|
19
|
-
GRAVATAR_STR || 'https://seccdn.libravatar.org/avatar/{{mail|md5}}';
|
|
27
|
+
const gravatarStr = GRAVATAR_STR || DEFAULT_GRAVATAR_STR;
|
|
20
28
|
return env.renderString(gravatarStr, comment);
|
|
21
29
|
}
|
|
22
30
|
};
|
|
@@ -28,10 +28,16 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
|
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
const sanitize = (content) =>
|
|
31
|
-
DOMPurify.sanitize(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
DOMPurify.sanitize(
|
|
32
|
+
content,
|
|
33
|
+
Object.assign(
|
|
34
|
+
{
|
|
35
|
+
FORBID_TAGS: ['form', 'input', 'style'],
|
|
36
|
+
FORBID_ATTR: ['autoplay', 'style'],
|
|
37
|
+
},
|
|
38
|
+
think.config('domPurify') || {}
|
|
39
|
+
)
|
|
40
|
+
);
|
|
35
41
|
|
|
36
42
|
module.exports = {
|
|
37
43
|
sanitize,
|
package/src/service/notify.js
CHANGED
|
@@ -273,6 +273,50 @@ module.exports = class extends think.Service {
|
|
|
273
273
|
});
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
+
async pushplus({ title, content }, self, parent) {
|
|
277
|
+
const {
|
|
278
|
+
PUSH_PLUS_KEY,
|
|
279
|
+
PUSH_PLUS_TOPIC: topic,
|
|
280
|
+
PUSH_PLUS_TEMPLATE: template,
|
|
281
|
+
PUSH_PLUS_CHANNEL: channel,
|
|
282
|
+
PUSH_PLUS_WEBHOOK: webhook,
|
|
283
|
+
PUSH_PLUS_CALLBACKURL: callbackUrl,
|
|
284
|
+
SITE_NAME,
|
|
285
|
+
SITE_URL,
|
|
286
|
+
} = process.env;
|
|
287
|
+
|
|
288
|
+
if (!PUSH_PLUS_KEY) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const data = {
|
|
293
|
+
self,
|
|
294
|
+
parent,
|
|
295
|
+
site: {
|
|
296
|
+
name: SITE_NAME,
|
|
297
|
+
url: SITE_URL,
|
|
298
|
+
postUrl: SITE_URL + self.url + '#' + self.objectId,
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
title = nunjucks.renderString(title, data);
|
|
302
|
+
content = nunjucks.renderString(content, data);
|
|
303
|
+
|
|
304
|
+
return request({
|
|
305
|
+
uri: `http://www.pushplus.plus/send/${PUSH_PLUS_KEY}`,
|
|
306
|
+
method: 'POST',
|
|
307
|
+
form: {
|
|
308
|
+
title,
|
|
309
|
+
content,
|
|
310
|
+
topic,
|
|
311
|
+
template,
|
|
312
|
+
channel,
|
|
313
|
+
webhook,
|
|
314
|
+
callbackUrl,
|
|
315
|
+
},
|
|
316
|
+
json: true,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
276
320
|
async run(comment, parent, disableAuthorNotify = false) {
|
|
277
321
|
const { AUTHOR_EMAIL, BLOGGER_EMAIL } = process.env;
|
|
278
322
|
const { mailSubject, mailTemplate, mailSubjectAdmin, mailTemplateAdmin } =
|
|
@@ -312,11 +356,10 @@ module.exports = class extends think.Service {
|
|
|
312
356
|
);
|
|
313
357
|
const qq = await this.qq(comment, parent);
|
|
314
358
|
const telegram = await this.telegram(comment, parent);
|
|
359
|
+
const pushplus = await this.pushplus({ title, content }, comment, parent);
|
|
360
|
+
console.log(pushplus);
|
|
315
361
|
if (
|
|
316
|
-
think.isEmpty
|
|
317
|
-
think.isEmpty(qq) &&
|
|
318
|
-
think.isEmpty(telegram) &&
|
|
319
|
-
think.isEmpty(qywxAmWechat) &&
|
|
362
|
+
[wechat, qq, telegram, qywxAmWechat, pushplus].every(think.isEmpty) &&
|
|
320
363
|
!isReplyAuthor
|
|
321
364
|
) {
|
|
322
365
|
mailList.push({ to: AUTHOR, title, content });
|
|
@@ -33,14 +33,17 @@ module.exports = class extends Base {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
parseWhere(where) {
|
|
37
37
|
if (think.isEmpty(where)) {
|
|
38
|
-
return
|
|
38
|
+
return {};
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const filter = {};
|
|
42
42
|
const parseKey = (k) => (k === 'objectId' ? '_id' : k);
|
|
43
43
|
for (let k in where) {
|
|
44
|
+
if (k === '_complex') {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
44
47
|
if (think.isString(where[k])) {
|
|
45
48
|
filter[parseKey(k)] = _.eq(where[k]);
|
|
46
49
|
continue;
|
|
@@ -84,7 +87,26 @@ module.exports = class extends Base {
|
|
|
84
87
|
}
|
|
85
88
|
}
|
|
86
89
|
}
|
|
87
|
-
return
|
|
90
|
+
return filter;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
where(instance, where) {
|
|
94
|
+
const filter = this.parseWhere(where);
|
|
95
|
+
if (!where._complex) {
|
|
96
|
+
return instance.where(filter);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const filters = [];
|
|
100
|
+
for (const k in where._complex) {
|
|
101
|
+
if (k === '_logic') {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
filters.push({
|
|
105
|
+
...this.parseWhere({ [k]: where._complex[k] }),
|
|
106
|
+
...filter,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return instance.where(_[where._complex._logic](...filters));
|
|
88
110
|
}
|
|
89
111
|
|
|
90
112
|
async _select(where, { desc, limit, offset, field } = {}) {
|
|
@@ -166,13 +166,18 @@ module.exports = class extends Base {
|
|
|
166
166
|
return this.git.set(filename, csv, { sha });
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
parseWhere(where) {
|
|
170
|
+
const _where = [];
|
|
170
171
|
if (think.isEmpty(where)) {
|
|
171
|
-
return
|
|
172
|
+
return _where;
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
const filters = [];
|
|
175
176
|
for (let k in where) {
|
|
177
|
+
if (k === '_complex') {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
176
181
|
if (k === 'objectId') {
|
|
177
182
|
filters.push((item) => item.id === where[k]);
|
|
178
183
|
continue;
|
|
@@ -219,7 +224,33 @@ module.exports = class extends Base {
|
|
|
219
224
|
}
|
|
220
225
|
}
|
|
221
226
|
|
|
222
|
-
return filters
|
|
227
|
+
return filters;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
where(data, where) {
|
|
231
|
+
const filter = this.parseWhere(where);
|
|
232
|
+
|
|
233
|
+
if (!where._complex) {
|
|
234
|
+
return data.filter((item) => filter.every((fn) => fn(item)));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const logicMap = {
|
|
238
|
+
and: Array.prototype.every,
|
|
239
|
+
or: Array.prototype.some,
|
|
240
|
+
};
|
|
241
|
+
const filters = [];
|
|
242
|
+
for (const k in where._complex) {
|
|
243
|
+
if (k === '_logic') {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
filters.push([...filter, ...this.parseWhere({ [k]: where._complex[k] })]);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const logicFn = logicMap[where._complex._logic];
|
|
251
|
+
return data.filter((item) =>
|
|
252
|
+
logicFn.call(filters, (filter) => filter.every((fn) => fn(item)))
|
|
253
|
+
);
|
|
223
254
|
}
|
|
224
255
|
|
|
225
256
|
async select(where, { desc, limit, offset, field } = {}) {
|
|
@@ -7,14 +7,18 @@ module.exports = class extends Base {
|
|
|
7
7
|
this.db = inspirecloud.db;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
parseWhere(where) {
|
|
11
|
+
const _where = {};
|
|
11
12
|
if (think.isEmpty(where)) {
|
|
12
|
-
return;
|
|
13
|
+
return _where;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
const _where = {};
|
|
16
16
|
const parseKey = (k) => (k === 'objectId' ? '_id' : k);
|
|
17
17
|
for (const k in where) {
|
|
18
|
+
if (k === '_complex') {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
if (think.isString(where[k])) {
|
|
19
23
|
_where[parseKey(k)] =
|
|
20
24
|
k === 'objectId' ? this.db.ObjectId(where[k]) : where[k];
|
|
@@ -28,18 +32,20 @@ module.exports = class extends Base {
|
|
|
28
32
|
const handler = where[k][0].toUpperCase();
|
|
29
33
|
switch (handler) {
|
|
30
34
|
case 'IN':
|
|
31
|
-
_where[parseKey(k)] =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
_where[parseKey(k)] = {
|
|
36
|
+
$in:
|
|
37
|
+
k === 'objectId'
|
|
38
|
+
? where[k][1].map(this.db.ObjectId)
|
|
39
|
+
: where[k][1],
|
|
40
|
+
};
|
|
36
41
|
break;
|
|
37
42
|
case 'NOT IN':
|
|
38
|
-
_where[parseKey(k)] =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
_where[parseKey(k)] = {
|
|
44
|
+
$nin:
|
|
45
|
+
k === 'objectId'
|
|
46
|
+
? where[k][1].map(this.db.ObjectId)
|
|
47
|
+
: where[k][1],
|
|
48
|
+
};
|
|
43
49
|
break;
|
|
44
50
|
case 'LIKE': {
|
|
45
51
|
const first = where[k][1][0];
|
|
@@ -52,14 +58,14 @@ module.exports = class extends Base {
|
|
|
52
58
|
} else if (last === '%') {
|
|
53
59
|
reg = new RegExp('^' + where[k][1].slice(0, -1));
|
|
54
60
|
}
|
|
55
|
-
_where[parseKey(k)] =
|
|
61
|
+
_where[parseKey(k)] = { $regex: reg };
|
|
56
62
|
break;
|
|
57
63
|
}
|
|
58
64
|
case '!=':
|
|
59
|
-
_where[parseKey(k)] =
|
|
65
|
+
_where[parseKey(k)] = { $ne: where[k] };
|
|
60
66
|
break;
|
|
61
67
|
case '>':
|
|
62
|
-
_where[parseKey(k)] =
|
|
68
|
+
_where[parseKey(k)] = { $gt: where[k] };
|
|
63
69
|
break;
|
|
64
70
|
}
|
|
65
71
|
}
|
|
@@ -69,6 +75,27 @@ module.exports = class extends Base {
|
|
|
69
75
|
return _where;
|
|
70
76
|
}
|
|
71
77
|
|
|
78
|
+
where(where) {
|
|
79
|
+
const filter = this.parseWhere(where);
|
|
80
|
+
if (!where._complex) {
|
|
81
|
+
return filter;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const filters = [];
|
|
85
|
+
for (const k in where._complex) {
|
|
86
|
+
if (k === '_logic') {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
filters.push({
|
|
91
|
+
...this.parseWhere({ [k]: where._complex[k] }),
|
|
92
|
+
...filter,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { [`$${where._complex._logic}`]: filters };
|
|
97
|
+
}
|
|
98
|
+
|
|
72
99
|
async _select(where, { desc, limit, offset, field } = {}) {
|
|
73
100
|
const instance = this.db.table(this.tableName);
|
|
74
101
|
const query = instance.where(this.where(where));
|
|
@@ -13,19 +13,26 @@ if (LEAN_ID && LEAN_KEY && LEAN_MASTER_KEY) {
|
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
module.exports = class extends Base {
|
|
16
|
-
|
|
16
|
+
parseWhere(className, where) {
|
|
17
|
+
const instance = new AV.Query(className);
|
|
17
18
|
if (think.isEmpty(where)) {
|
|
18
|
-
return;
|
|
19
|
+
return instance;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
for (const k in where) {
|
|
23
|
+
if (k === '_complex') {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
if (think.isString(where[k])) {
|
|
23
28
|
instance.equalTo(k, where[k]);
|
|
24
29
|
continue;
|
|
25
30
|
}
|
|
31
|
+
|
|
26
32
|
if (where[k] === undefined) {
|
|
27
33
|
instance.doesNotExist(k);
|
|
28
34
|
}
|
|
35
|
+
|
|
29
36
|
if (Array.isArray(where[k])) {
|
|
30
37
|
if (where[k][0]) {
|
|
31
38
|
const handler = where[k][0].toUpperCase();
|
|
@@ -58,11 +65,32 @@ module.exports = class extends Base {
|
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
}
|
|
68
|
+
return instance;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
where(className, where) {
|
|
72
|
+
if (!where._complex) {
|
|
73
|
+
return this.parseWhere(className, where);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const filters = [];
|
|
77
|
+
for (const k in where._complex) {
|
|
78
|
+
if (k === '_logic') {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const filter = this.parseWhere(className, {
|
|
83
|
+
...where,
|
|
84
|
+
[k]: where._complex[k],
|
|
85
|
+
});
|
|
86
|
+
filters.push(filter);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return AV.Query[where._complex._logic](...filters);
|
|
61
90
|
}
|
|
62
91
|
|
|
63
92
|
async _select(where, { desc, limit, offset, field } = {}) {
|
|
64
|
-
const instance =
|
|
65
|
-
this.where(instance, where);
|
|
93
|
+
const instance = this.where(this.tableName, where);
|
|
66
94
|
if (desc) {
|
|
67
95
|
instance.descending(desc);
|
|
68
96
|
}
|
|
@@ -99,8 +127,7 @@ module.exports = class extends Base {
|
|
|
99
127
|
}
|
|
100
128
|
|
|
101
129
|
async count(where = {}, options = {}) {
|
|
102
|
-
const instance =
|
|
103
|
-
this.where(instance, where);
|
|
130
|
+
const instance = this.where(this.tableName, where);
|
|
104
131
|
return instance.count(options).catch((e) => {
|
|
105
132
|
if (e.code === 101) {
|
|
106
133
|
return 0;
|
|
@@ -127,8 +154,7 @@ module.exports = class extends Base {
|
|
|
127
154
|
}
|
|
128
155
|
|
|
129
156
|
async update(data, where) {
|
|
130
|
-
const instance =
|
|
131
|
-
this.where(instance, where);
|
|
157
|
+
const instance = this.where(this.tableName, where);
|
|
132
158
|
const ret = await instance.find();
|
|
133
159
|
|
|
134
160
|
return Promise.all(
|
|
@@ -146,8 +172,7 @@ module.exports = class extends Base {
|
|
|
146
172
|
}
|
|
147
173
|
|
|
148
174
|
async delete(where) {
|
|
149
|
-
const instance =
|
|
150
|
-
this.where(instance, where);
|
|
175
|
+
const instance = this.where(this.tableName, where);
|
|
151
176
|
const data = await instance.find();
|
|
152
177
|
|
|
153
178
|
return AV.Object.destroyAll(data);
|
|
@@ -2,25 +2,25 @@ const { ObjectId } = require('mongodb');
|
|
|
2
2
|
const Base = require('./base');
|
|
3
3
|
|
|
4
4
|
module.exports = class extends Base {
|
|
5
|
-
|
|
5
|
+
parseWhere(where) {
|
|
6
6
|
if (think.isEmpty(where)) {
|
|
7
|
-
return;
|
|
7
|
+
return {};
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
const filter = {};
|
|
10
11
|
const parseKey = (k) => (k === 'objectId' ? '_id' : k);
|
|
11
12
|
for (let k in where) {
|
|
13
|
+
if (k === '_complex') {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
12
16
|
if (think.isString(where[k])) {
|
|
13
|
-
|
|
14
|
-
[
|
|
15
|
-
|
|
16
|
-
},
|
|
17
|
-
});
|
|
17
|
+
filter[parseKey(k)] = {
|
|
18
|
+
$eq: k === 'objectId' ? ObjectId(where[k]) : where[k],
|
|
19
|
+
};
|
|
18
20
|
continue;
|
|
19
21
|
}
|
|
20
22
|
if (where[k] === undefined) {
|
|
21
|
-
|
|
22
|
-
[parseKey(k)]: { $eq: null },
|
|
23
|
-
});
|
|
23
|
+
filter[parseKey(k)] = { $eq: null };
|
|
24
24
|
}
|
|
25
25
|
if (Array.isArray(where[k])) {
|
|
26
26
|
if (where[k][0]) {
|
|
@@ -28,63 +28,70 @@ module.exports = class extends Base {
|
|
|
28
28
|
switch (handler) {
|
|
29
29
|
case 'IN':
|
|
30
30
|
if (k === 'objectId') {
|
|
31
|
-
|
|
32
|
-
[parseKey(k)]: { $in: where[k][1].map(ObjectId) },
|
|
33
|
-
});
|
|
31
|
+
filter[parseKey(k)] = { $in: where[k][1].map(ObjectId) };
|
|
34
32
|
} else {
|
|
35
|
-
|
|
36
|
-
[
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
});
|
|
33
|
+
filter[parseKey(k)] = {
|
|
34
|
+
$regex: new RegExp(`^(${where[k][1].join('|')})$`),
|
|
35
|
+
};
|
|
40
36
|
}
|
|
41
37
|
break;
|
|
42
38
|
case 'NOT IN':
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
},
|
|
48
|
-
});
|
|
39
|
+
filter[parseKey(k)] = {
|
|
40
|
+
$nin:
|
|
41
|
+
k === 'objectId' ? where[k][1].map(ObjectId) : where[k][1],
|
|
42
|
+
};
|
|
49
43
|
break;
|
|
50
44
|
case 'LIKE': {
|
|
51
45
|
const first = where[k][1][0];
|
|
52
46
|
const last = where[k][1].slice(-1);
|
|
47
|
+
let reg;
|
|
53
48
|
if (first === '%' && last === '%') {
|
|
54
|
-
|
|
55
|
-
[parseKey(k)]: {
|
|
56
|
-
$regex: new RegExp(where[k][1].slice(1, -1)),
|
|
57
|
-
},
|
|
58
|
-
});
|
|
49
|
+
reg = new RegExp(where[k][1].slice(1, -1));
|
|
59
50
|
} else if (first === '%') {
|
|
60
|
-
|
|
61
|
-
[parseKey(k)]: {
|
|
62
|
-
$regex: new RegExp(where[k][1].slice(1) + '$'),
|
|
63
|
-
},
|
|
64
|
-
});
|
|
51
|
+
reg = new RegExp(where[k][1].slice(1) + '$');
|
|
65
52
|
} else if (last === '%') {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
53
|
+
reg = new RegExp('^' + where[k][1].slice(0, -1));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (reg) {
|
|
57
|
+
filter[parseKey(k)] = { $regex: reg };
|
|
71
58
|
}
|
|
72
59
|
break;
|
|
73
60
|
}
|
|
74
61
|
case '!=':
|
|
75
|
-
|
|
76
|
-
[parseKey(k)]: { $ne: where[k][1] },
|
|
77
|
-
});
|
|
62
|
+
filter[parseKey(k)] = { $ne: where[k][1] };
|
|
78
63
|
break;
|
|
79
64
|
case '>':
|
|
80
|
-
|
|
81
|
-
[parseKey(k)]: { $gt: where[k][1] },
|
|
82
|
-
});
|
|
65
|
+
filter[parseKey(k)] = { $gt: where[k][1] };
|
|
83
66
|
break;
|
|
84
67
|
}
|
|
85
68
|
}
|
|
86
69
|
}
|
|
87
70
|
}
|
|
71
|
+
return filter;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
where(instance, where) {
|
|
75
|
+
const filter = this.parseWhere(where);
|
|
76
|
+
if (!where._complex) {
|
|
77
|
+
return instance.where(filter);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const filters = [];
|
|
81
|
+
for (const k in where._complex) {
|
|
82
|
+
if (k === '_logic') {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
filters.push({
|
|
86
|
+
...this.parseWhere({ [k]: where._complex[k] }),
|
|
87
|
+
...filter,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return instance.where({
|
|
92
|
+
// $or, $and, $not, $nor
|
|
93
|
+
[`$${where._complex._logic}`]: filters,
|
|
94
|
+
});
|
|
88
95
|
}
|
|
89
96
|
|
|
90
97
|
async select(where, { desc, limit, offset, field } = {}) {
|