@waline/vercel 1.5.0 → 1.7.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/package.json +2 -2
- package/src/config/config.js +3 -1
- package/src/controller/comment.js +110 -57
- package/src/logic/base.js +1 -0
- package/src/service/notify.js +84 -2
- 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/vercel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "vercel server for waline comment system",
|
|
5
5
|
"repository": "https://github.com/walinejs/waline",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"jsonwebtoken": "^8.5.1",
|
|
18
18
|
"katex": "^0.13.23",
|
|
19
19
|
"leancloud-storage": "^4.12.0",
|
|
20
|
-
"markdown-it": "^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",
|
package/src/config/config.js
CHANGED
|
@@ -36,6 +36,7 @@ const {
|
|
|
36
36
|
MAIL_TEMPLATE_ADMIN,
|
|
37
37
|
QQ_TEMPLATE,
|
|
38
38
|
TG_TEMPLATE,
|
|
39
|
+
WX_TEMPLATE,
|
|
39
40
|
} = process.env;
|
|
40
41
|
|
|
41
42
|
let storage = 'leancloud';
|
|
@@ -105,7 +106,7 @@ module.exports = {
|
|
|
105
106
|
forbiddenWords,
|
|
106
107
|
disallowIPList: [],
|
|
107
108
|
secureDomains: SECURE_DOMAINS ? SECURE_DOMAINS.split(/\s*,\s*/) : undefined,
|
|
108
|
-
disableUserAgent: !isFalse(DISABLE_USERAGENT),
|
|
109
|
+
disableUserAgent: DISABLE_USERAGENT && !isFalse(DISABLE_USERAGENT),
|
|
109
110
|
avatarProxy,
|
|
110
111
|
oauthUrl,
|
|
111
112
|
markdown,
|
|
@@ -115,4 +116,5 @@ module.exports = {
|
|
|
115
116
|
mailTemplateAdmin: MAIL_TEMPLATE_ADMIN,
|
|
116
117
|
QQTemplate: QQ_TEMPLATE,
|
|
117
118
|
TGTemplate: TG_TEMPLATE,
|
|
119
|
+
WXTemplate: WX_TEMPLATE,
|
|
118
120
|
};
|
|
@@ -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
package/src/service/notify.js
CHANGED
|
@@ -91,6 +91,81 @@ module.exports = class extends think.Service {
|
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
async qywxAmWechat({ title, content }, self, parent) {
|
|
95
|
+
const { QYWX_AM, SITE_NAME, SITE_URL } = process.env;
|
|
96
|
+
if (!QYWX_AM) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const QYWX_AM_AY = QYWX_AM.split(',');
|
|
101
|
+
const comment = self.comment
|
|
102
|
+
.replace(/<a href="(.*?)">(.*?)<\/a>/g, '\n[$2] $1\n')
|
|
103
|
+
.replace(/<[^>]+>/g, '');
|
|
104
|
+
const postName = self.url;
|
|
105
|
+
|
|
106
|
+
const data = {
|
|
107
|
+
self: {
|
|
108
|
+
...self,
|
|
109
|
+
comment,
|
|
110
|
+
},
|
|
111
|
+
postName,
|
|
112
|
+
parent,
|
|
113
|
+
site: {
|
|
114
|
+
name: SITE_NAME,
|
|
115
|
+
url: SITE_URL,
|
|
116
|
+
postUrl: SITE_URL + self.url + '#' + self.objectId,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
const contentWechat =
|
|
120
|
+
think.config('WXTemplate') ||
|
|
121
|
+
`💬 {{site.name|safe}}的文章《{{postName}}》有新评论啦
|
|
122
|
+
【评论者昵称】:{{self.nick}}
|
|
123
|
+
【评论者邮箱】:{{self.mail}}
|
|
124
|
+
【内容】:{{self.comment}}
|
|
125
|
+
<a href='{{site.postUrl}}'>查看详情</a>`;
|
|
126
|
+
|
|
127
|
+
title = nunjucks.renderString(title, data);
|
|
128
|
+
const desp = nunjucks.renderString(contentWechat, data);
|
|
129
|
+
content = desp.replace(/\n/g, '<br/>');
|
|
130
|
+
|
|
131
|
+
const { access_token } = await request({
|
|
132
|
+
uri: `https://qyapi.weixin.qq.com/cgi-bin/gettoken`,
|
|
133
|
+
qs: {
|
|
134
|
+
corpid: `${QYWX_AM_AY[0]}`,
|
|
135
|
+
corpsecret: `${QYWX_AM_AY[1]}`,
|
|
136
|
+
},
|
|
137
|
+
headers: {
|
|
138
|
+
'Content-Type': 'application/json',
|
|
139
|
+
},
|
|
140
|
+
json: true,
|
|
141
|
+
});
|
|
142
|
+
return request({
|
|
143
|
+
url: `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${access_token}`,
|
|
144
|
+
body: {
|
|
145
|
+
touser: `${QYWX_AM_AY[2]}`,
|
|
146
|
+
agentid: `${QYWX_AM_AY[3]}`,
|
|
147
|
+
msgtype: 'mpnews',
|
|
148
|
+
mpnews: {
|
|
149
|
+
articles: [
|
|
150
|
+
{
|
|
151
|
+
title,
|
|
152
|
+
thumb_media_id: `${QYWX_AM_AY[4]}`,
|
|
153
|
+
author: `Waline Comment`,
|
|
154
|
+
content_source_url: `${data.site.postUrl}`,
|
|
155
|
+
content: `${content}`,
|
|
156
|
+
digest: `${desp}`,
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
method: 'POST',
|
|
162
|
+
json: true,
|
|
163
|
+
headers: {
|
|
164
|
+
'Content-Type': 'application/json',
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
94
169
|
async qq(self, parent) {
|
|
95
170
|
const { QMSG_KEY, QQ_ID, SITE_NAME, SITE_URL } = process.env;
|
|
96
171
|
if (!QMSG_KEY) {
|
|
@@ -212,7 +287,7 @@ module.exports = class extends think.Service {
|
|
|
212
287
|
? parent && parent.mail.toLowerCase() === AUTHOR.toLowerCase()
|
|
213
288
|
: false;
|
|
214
289
|
|
|
215
|
-
const title = mailSubjectAdmin || '{{site.name}} 上有新评论了';
|
|
290
|
+
const title = mailSubjectAdmin || '{{site.name | safe}} 上有新评论了';
|
|
216
291
|
const content =
|
|
217
292
|
mailTemplateAdmin ||
|
|
218
293
|
`
|
|
@@ -230,12 +305,18 @@ module.exports = class extends think.Service {
|
|
|
230
305
|
|
|
231
306
|
if (!isAuthorComment && !disableAuthorNotify) {
|
|
232
307
|
const wechat = await this.wechat({ title, content }, comment, parent);
|
|
308
|
+
const qywxAmWechat = await this.qywxAmWechat(
|
|
309
|
+
{ title, content },
|
|
310
|
+
comment,
|
|
311
|
+
parent
|
|
312
|
+
);
|
|
233
313
|
const qq = await this.qq(comment, parent);
|
|
234
314
|
const telegram = await this.telegram(comment, parent);
|
|
235
315
|
if (
|
|
236
316
|
think.isEmpty(wechat) &&
|
|
237
317
|
think.isEmpty(qq) &&
|
|
238
318
|
think.isEmpty(telegram) &&
|
|
319
|
+
think.isEmpty(qywxAmWechat) &&
|
|
239
320
|
!isReplyAuthor
|
|
240
321
|
) {
|
|
241
322
|
mailList.push({ to: AUTHOR, title, content });
|
|
@@ -250,7 +331,8 @@ module.exports = class extends think.Service {
|
|
|
250
331
|
mailList.push({
|
|
251
332
|
to: parent.mail,
|
|
252
333
|
title:
|
|
253
|
-
mailSubject ||
|
|
334
|
+
mailSubject ||
|
|
335
|
+
'{{parent.nick | safe}},『{{site.name | safe}}』上的评论收到了回复',
|
|
254
336
|
content:
|
|
255
337
|
mailTemplate ||
|
|
256
338
|
`
|
|
@@ -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 } = {}) {
|