@waline/vercel 1.6.0 → 1.8.0
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 +11 -11
- package/src/config/adapter.js +3 -4
- package/src/controller/comment.js +110 -57
- package/src/logic/base.js +1 -0
- package/src/service/markdown/xss.js +10 -4
- package/src/service/notify.js +50 -6
- package/src/service/storage/cloudbase.js +27 -4
- package/src/service/storage/github.js +34 -3
- package/src/service/storage/inspirecloud.js +45 -17
- package/src/service/storage/leancloud.js +37 -11
- 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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/vercel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "vercel server for waline comment system",
|
|
5
5
|
"repository": "https://github.com/walinejs/waline",
|
|
6
6
|
"license": "MIT",
|
|
@@ -10,25 +10,25 @@
|
|
|
10
10
|
"@cloudbase/node-sdk": "^2.7.1",
|
|
11
11
|
"@koa/cors": "^3.1.0",
|
|
12
12
|
"akismet": "^2.0.6",
|
|
13
|
-
"deta": "^1.0.
|
|
14
|
-
"dompurify": "^2.3.
|
|
13
|
+
"deta": "^1.0.2",
|
|
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.2",
|
|
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.2",
|
|
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
33
|
"think-model-mysql": "^1.1.6",
|
|
34
34
|
"think-model-postgresql": "^1.1.6",
|
|
@@ -36,6 +36,6 @@
|
|
|
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
|
);
|
|
@@ -154,40 +173,67 @@ module.exports = class extends BaseRest {
|
|
|
154
173
|
offset: Math.max((page - 1) * pageSize, 0),
|
|
155
174
|
});
|
|
156
175
|
|
|
176
|
+
const userModel = this.service(
|
|
177
|
+
`storage/${this.config('storage')}`,
|
|
178
|
+
'Users'
|
|
179
|
+
);
|
|
180
|
+
const user_ids = Array.from(
|
|
181
|
+
new Set(comments.map(({ user_id }) => user_id).filter((v) => v))
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
let users = [];
|
|
185
|
+
if (user_ids.length) {
|
|
186
|
+
users = await userModel.select(
|
|
187
|
+
{ objectId: ['IN', user_ids] },
|
|
188
|
+
{
|
|
189
|
+
field: ['display_name', 'email', 'url', 'type', 'avatar'],
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
157
194
|
return this.success({
|
|
158
195
|
page,
|
|
159
196
|
totalPages: Math.ceil(count / pageSize),
|
|
160
197
|
pageSize,
|
|
161
198
|
spamCount,
|
|
162
199
|
waitingCount,
|
|
163
|
-
data:
|
|
200
|
+
data: await Promise.all(
|
|
201
|
+
comments.map((cmt) =>
|
|
202
|
+
formatCmt(cmt, users, this.config(), userInfo)
|
|
203
|
+
)
|
|
204
|
+
),
|
|
164
205
|
});
|
|
165
206
|
}
|
|
166
207
|
|
|
167
208
|
default: {
|
|
168
209
|
const { path: url, page, pageSize } = this.get();
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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',
|
|
173
216
|
status: ['NOT IN', ['waiting', 'spam']],
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
+
});
|
|
191
237
|
|
|
192
238
|
const userModel = this.service(
|
|
193
239
|
`storage/${this.config('storage')}`,
|
|
@@ -221,11 +267,16 @@ module.exports = class extends BaseRest {
|
|
|
221
267
|
count: comments.length,
|
|
222
268
|
data: await Promise.all(
|
|
223
269
|
rootComments.map(async (comment) => {
|
|
224
|
-
const cmt = await formatCmt(
|
|
270
|
+
const cmt = await formatCmt(
|
|
271
|
+
comment,
|
|
272
|
+
users,
|
|
273
|
+
this.config(),
|
|
274
|
+
userInfo
|
|
275
|
+
);
|
|
225
276
|
cmt.children = await Promise.all(
|
|
226
277
|
comments
|
|
227
278
|
.filter(({ rid }) => rid === cmt.objectId)
|
|
228
|
-
.map((cmt) => formatCmt(cmt, users, this.config()))
|
|
279
|
+
.map((cmt) => formatCmt(cmt, users, this.config(), userInfo))
|
|
229
280
|
.reverse()
|
|
230
281
|
);
|
|
231
282
|
return cmt;
|
|
@@ -383,7 +434,9 @@ module.exports = class extends BaseRest {
|
|
|
383
434
|
|
|
384
435
|
think.logger.debug(`Comment post hooks postSave done!`);
|
|
385
436
|
|
|
386
|
-
return this.success(
|
|
437
|
+
return this.success(
|
|
438
|
+
await formatCmt(resp, [userInfo], this.config(), userInfo)
|
|
439
|
+
);
|
|
387
440
|
}
|
|
388
441
|
|
|
389
442
|
async putAction() {
|
package/src/logic/base.js
CHANGED
|
@@ -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 } =
|
|
@@ -287,7 +331,7 @@ module.exports = class extends think.Service {
|
|
|
287
331
|
? parent && parent.mail.toLowerCase() === AUTHOR.toLowerCase()
|
|
288
332
|
: false;
|
|
289
333
|
|
|
290
|
-
const title = mailSubjectAdmin || '{{site.name}} 上有新评论了';
|
|
334
|
+
const title = mailSubjectAdmin || '{{site.name | safe}} 上有新评论了';
|
|
291
335
|
const content =
|
|
292
336
|
mailTemplateAdmin ||
|
|
293
337
|
`
|
|
@@ -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 });
|
|
@@ -331,7 +374,8 @@ module.exports = class extends think.Service {
|
|
|
331
374
|
mailList.push({
|
|
332
375
|
to: parent.mail,
|
|
333
376
|
title:
|
|
334
|
-
mailSubject ||
|
|
377
|
+
mailSubject ||
|
|
378
|
+
'{{parent.nick | safe}},『{{site.name | safe}}』上的评论收到了回复',
|
|
335
379
|
content:
|
|
336
380
|
mailTemplate ||
|
|
337
381
|
`
|
|
@@ -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 } = {}) {
|
|
@@ -115,8 +137,9 @@ module.exports = class extends Base {
|
|
|
115
137
|
async select(where, options = {}) {
|
|
116
138
|
let data = [];
|
|
117
139
|
let ret = [];
|
|
140
|
+
let offset = options.offset || 0;
|
|
118
141
|
do {
|
|
119
|
-
options.offset =
|
|
142
|
+
options.offset = offset + data.length;
|
|
120
143
|
ret = await this._select(where, options);
|
|
121
144
|
data = data.concat(ret);
|
|
122
145
|
} while (ret.length === 100);
|
|
@@ -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));
|
|
@@ -101,8 +128,9 @@ module.exports = class extends Base {
|
|
|
101
128
|
async select(where, options = {}) {
|
|
102
129
|
let data = [];
|
|
103
130
|
let ret = [];
|
|
131
|
+
let offset = options.offset || 0;
|
|
104
132
|
do {
|
|
105
|
-
options.offset =
|
|
133
|
+
options.offset = offset + data.length;
|
|
106
134
|
ret = await this._select(where, options);
|
|
107
135
|
data = data.concat(ret);
|
|
108
136
|
} while (ret.length === 1000);
|
|
@@ -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
|
}
|
|
@@ -88,8 +116,9 @@ module.exports = class extends Base {
|
|
|
88
116
|
async select(where, options = {}) {
|
|
89
117
|
let data = [];
|
|
90
118
|
let ret = [];
|
|
119
|
+
let offset = options.offset || 0;
|
|
91
120
|
do {
|
|
92
|
-
options.offset =
|
|
121
|
+
options.offset = offset + data.length;
|
|
93
122
|
ret = await this._select(where, options);
|
|
94
123
|
data = data.concat(ret);
|
|
95
124
|
} while (ret.length === 100);
|
|
@@ -98,8 +127,7 @@ module.exports = class extends Base {
|
|
|
98
127
|
}
|
|
99
128
|
|
|
100
129
|
async count(where = {}, options = {}) {
|
|
101
|
-
const instance =
|
|
102
|
-
this.where(instance, where);
|
|
130
|
+
const instance = this.where(this.tableName, where);
|
|
103
131
|
return instance.count(options).catch((e) => {
|
|
104
132
|
if (e.code === 101) {
|
|
105
133
|
return 0;
|
|
@@ -126,8 +154,7 @@ module.exports = class extends Base {
|
|
|
126
154
|
}
|
|
127
155
|
|
|
128
156
|
async update(data, where) {
|
|
129
|
-
const instance =
|
|
130
|
-
this.where(instance, where);
|
|
157
|
+
const instance = this.where(this.tableName, where);
|
|
131
158
|
const ret = await instance.find();
|
|
132
159
|
|
|
133
160
|
return Promise.all(
|
|
@@ -145,8 +172,7 @@ module.exports = class extends Base {
|
|
|
145
172
|
}
|
|
146
173
|
|
|
147
174
|
async delete(where) {
|
|
148
|
-
const instance =
|
|
149
|
-
this.where(instance, where);
|
|
175
|
+
const instance = this.where(this.tableName, where);
|
|
150
176
|
const data = await instance.find();
|
|
151
177
|
|
|
152
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 } = {}) {
|