@waline/vercel 1.7.0 → 1.9.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 +13 -14
- package/src/config/adapter.js +3 -4
- package/src/config/config.js +0 -4
- package/src/controller/comment.js +13 -4
- package/src/controller/db.js +56 -0
- package/src/controller/index.js +3 -2
- package/src/logic/base.js +7 -1
- package/src/logic/db.js +30 -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/inspirecloud.js +0 -180
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,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/vercel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
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
|
-
"@
|
|
10
|
-
"@
|
|
11
|
-
"@koa/cors": "^3.1.0",
|
|
9
|
+
"@cloudbase/node-sdk": "^2.9.0",
|
|
10
|
+
"@koa/cors": "^3.2.0",
|
|
12
11
|
"akismet": "^2.0.6",
|
|
13
|
-
"deta": "^1.0
|
|
14
|
-
"dompurify": "^2.3.
|
|
12
|
+
"deta": "^1.1.0",
|
|
13
|
+
"dompurify": "^2.3.6",
|
|
15
14
|
"fast-csv": "^4.3.6",
|
|
16
|
-
"jsdom": "^
|
|
15
|
+
"jsdom": "^19.0.0",
|
|
17
16
|
"jsonwebtoken": "^8.5.1",
|
|
18
|
-
"katex": "^0.
|
|
19
|
-
"leancloud-storage": "^4.12.
|
|
17
|
+
"katex": "^0.15.3",
|
|
18
|
+
"leancloud-storage": "^4.12.2",
|
|
20
19
|
"markdown-it": "^12.3.2",
|
|
21
20
|
"markdown-it-emoji": "^2.0.0",
|
|
22
21
|
"markdown-it-sub": "^1.0.0",
|
|
23
22
|
"markdown-it-sup": "^1.0.0",
|
|
24
23
|
"mathjax-full": "^3.2.0",
|
|
25
|
-
"nodemailer": "^6.7.
|
|
24
|
+
"nodemailer": "^6.7.3",
|
|
26
25
|
"nunjucks": "^3.2.3",
|
|
27
26
|
"phpass": "^0.1.1",
|
|
28
|
-
"prismjs": "^1.
|
|
27
|
+
"prismjs": "^1.27.0",
|
|
29
28
|
"request": "^2.88.2",
|
|
30
29
|
"request-promise-native": "^1.0.9",
|
|
31
|
-
"think-logger3": "^1.
|
|
30
|
+
"think-logger3": "^1.3.1",
|
|
32
31
|
"think-model": "^1.5.4",
|
|
33
|
-
"think-model-mysql": "^1.1.
|
|
32
|
+
"think-model-mysql": "^1.1.7",
|
|
34
33
|
"think-model-postgresql": "^1.1.6",
|
|
35
34
|
"think-model-sqlite": "^1.2.2",
|
|
36
35
|
"think-mongo": "^2.1.2",
|
|
37
36
|
"think-router-rest": "^1.0.5",
|
|
38
37
|
"thinkjs": "^3.2.14",
|
|
39
|
-
"ua-parser-js": "^0.
|
|
38
|
+
"ua-parser-js": "^1.0.2"
|
|
40
39
|
}
|
|
41
40
|
}
|
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';
|
package/src/config/config.js
CHANGED
|
@@ -17,7 +17,6 @@ const {
|
|
|
17
17
|
AVATAR_PROXY,
|
|
18
18
|
GITHUB_TOKEN,
|
|
19
19
|
DETA_PROJECT_KEY,
|
|
20
|
-
INSPIRECLOUD_SERVICE_SECRET,
|
|
21
20
|
OAUTH_URL,
|
|
22
21
|
|
|
23
22
|
MARKDOWN_CONFIG = '{}',
|
|
@@ -64,9 +63,6 @@ if (LEAN_KEY) {
|
|
|
64
63
|
} else if (DETA_PROJECT_KEY) {
|
|
65
64
|
storage = 'deta';
|
|
66
65
|
jwtKey = jwtKey || DETA_PROJECT_KEY;
|
|
67
|
-
} else if (INSPIRECLOUD_SERVICE_SECRET) {
|
|
68
|
-
storage = 'inspirecloud';
|
|
69
|
-
jwtKey = jwtKey || INSPIRECLOUD_SERVICE_SECRET;
|
|
70
66
|
}
|
|
71
67
|
|
|
72
68
|
if (think.env === 'cloudbase' && storage === 'sqlite') {
|
|
@@ -441,7 +441,12 @@ module.exports = class extends BaseRest {
|
|
|
441
441
|
|
|
442
442
|
async putAction() {
|
|
443
443
|
const data = this.post();
|
|
444
|
-
|
|
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];
|
|
445
450
|
const preUpdateResp = await this.hook('preUpdate', {
|
|
446
451
|
...data,
|
|
447
452
|
objectId: this.id,
|
|
@@ -451,18 +456,22 @@ module.exports = class extends BaseRest {
|
|
|
451
456
|
return this.fail(preUpdateResp);
|
|
452
457
|
}
|
|
453
458
|
|
|
454
|
-
await this.modelInstance.update(data, {
|
|
459
|
+
const newData = await this.modelInstance.update(data, {
|
|
460
|
+
objectId: this.id,
|
|
461
|
+
});
|
|
455
462
|
|
|
456
463
|
if (
|
|
457
464
|
oldData.status === 'waiting' &&
|
|
458
465
|
data.status === 'approved' &&
|
|
459
466
|
oldData.pid
|
|
460
467
|
) {
|
|
461
|
-
let pComment = await this.modelInstance.select({
|
|
468
|
+
let pComment = await this.modelInstance.select({
|
|
469
|
+
objectId: oldData.pid,
|
|
470
|
+
});
|
|
462
471
|
pComment = pComment[0];
|
|
463
472
|
|
|
464
473
|
const notify = this.service('notify');
|
|
465
|
-
await notify.run(
|
|
474
|
+
await notify.run(newData, pComment, true);
|
|
466
475
|
}
|
|
467
476
|
|
|
468
477
|
await this.hook('postUpdate', data);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const fs = require('fs/promises');
|
|
2
|
+
const BaseRest = require('./rest');
|
|
3
|
+
|
|
4
|
+
module.exports = class extends BaseRest {
|
|
5
|
+
async getAction() {
|
|
6
|
+
const exportData = {
|
|
7
|
+
type: 'waline',
|
|
8
|
+
version: 1,
|
|
9
|
+
time: Date.now(),
|
|
10
|
+
tables: ['Comment', 'Counter', 'Users'],
|
|
11
|
+
data: {
|
|
12
|
+
Comment: [],
|
|
13
|
+
Counter: [],
|
|
14
|
+
Users: [],
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < exportData.tables.length; i++) {
|
|
19
|
+
const tableName = exportData.tables[i];
|
|
20
|
+
const model = this.model(tableName);
|
|
21
|
+
const data = await model.select({});
|
|
22
|
+
exportData.data[tableName] = data;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return this.success(exportData);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async postAction() {
|
|
29
|
+
const file = this.file('file');
|
|
30
|
+
try {
|
|
31
|
+
const jsonText = await fs.readFile(file.path, 'utf-8');
|
|
32
|
+
const importData = JSON.parse(jsonText);
|
|
33
|
+
if (!importData || importData.type !== 'waline') {
|
|
34
|
+
return this.fail('import data format not support!');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < importData.tables.length; i++) {
|
|
38
|
+
const tableName = importData.tables[i];
|
|
39
|
+
const model = this.model(tableName);
|
|
40
|
+
|
|
41
|
+
// delete all data at first
|
|
42
|
+
await model.delete({});
|
|
43
|
+
// then add data one by one
|
|
44
|
+
for (let j = 0; j < importData.data[tableName].length; j++) {
|
|
45
|
+
await model.add(importData.data[tableName][j]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return this.success();
|
|
49
|
+
} catch (e) {
|
|
50
|
+
if (think.isPrevent(e)) {
|
|
51
|
+
return this.success();
|
|
52
|
+
}
|
|
53
|
+
return this.fail(e.message);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
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
|
@@ -17,7 +17,13 @@ module.exports = class extends think.Logic {
|
|
|
17
17
|
secureDomains = think.isArray(secureDomains)
|
|
18
18
|
? secureDomains
|
|
19
19
|
: [secureDomains];
|
|
20
|
-
secureDomains.push(
|
|
20
|
+
secureDomains.push(
|
|
21
|
+
'localhost',
|
|
22
|
+
'127.0.0.1',
|
|
23
|
+
'github.com',
|
|
24
|
+
'api.twitter.com',
|
|
25
|
+
'www.facebook.com'
|
|
26
|
+
);
|
|
21
27
|
|
|
22
28
|
const match = secureDomains.some((domain) =>
|
|
23
29
|
think.isFunction(domain.test)
|
package/src/logic/db.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const Base = require('./base');
|
|
2
|
+
|
|
3
|
+
module.exports = class extends Base {
|
|
4
|
+
async __before(...args) {
|
|
5
|
+
await super.__before(...args);
|
|
6
|
+
|
|
7
|
+
const { userInfo } = this.ctx.state;
|
|
8
|
+
if (think.isEmpty(userInfo)) {
|
|
9
|
+
return this.fail(401);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (userInfo.type !== 'administrator') {
|
|
13
|
+
return this.fail(403);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @api {GET} /db export site data
|
|
19
|
+
* @apiGroup Site
|
|
20
|
+
* @apiVersion 0.0.1
|
|
21
|
+
*/
|
|
22
|
+
async getAction() {}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @api {GET} /db import site data
|
|
26
|
+
* @apiGroup Site
|
|
27
|
+
* @apiVersion 0.0.1
|
|
28
|
+
*/
|
|
29
|
+
async postAction() {}
|
|
30
|
+
};
|
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 });
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
const inspirecloud = require('@byteinspire/api');
|
|
2
|
-
const Base = require('./base');
|
|
3
|
-
|
|
4
|
-
module.exports = class extends Base {
|
|
5
|
-
constructor(tableName) {
|
|
6
|
-
super(tableName);
|
|
7
|
-
this.db = inspirecloud.db;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
parseWhere(where) {
|
|
11
|
-
const _where = {};
|
|
12
|
-
if (think.isEmpty(where)) {
|
|
13
|
-
return _where;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const parseKey = (k) => (k === 'objectId' ? '_id' : k);
|
|
17
|
-
for (const k in where) {
|
|
18
|
-
if (k === '_complex') {
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (think.isString(where[k])) {
|
|
23
|
-
_where[parseKey(k)] =
|
|
24
|
-
k === 'objectId' ? this.db.ObjectId(where[k]) : where[k];
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
if (where[k] === undefined) {
|
|
28
|
-
_where[parseKey(k)] = undefined;
|
|
29
|
-
}
|
|
30
|
-
if (Array.isArray(where[k])) {
|
|
31
|
-
if (where[k][0]) {
|
|
32
|
-
const handler = where[k][0].toUpperCase();
|
|
33
|
-
switch (handler) {
|
|
34
|
-
case 'IN':
|
|
35
|
-
_where[parseKey(k)] = {
|
|
36
|
-
$in:
|
|
37
|
-
k === 'objectId'
|
|
38
|
-
? where[k][1].map(this.db.ObjectId)
|
|
39
|
-
: where[k][1],
|
|
40
|
-
};
|
|
41
|
-
break;
|
|
42
|
-
case 'NOT IN':
|
|
43
|
-
_where[parseKey(k)] = {
|
|
44
|
-
$nin:
|
|
45
|
-
k === 'objectId'
|
|
46
|
-
? where[k][1].map(this.db.ObjectId)
|
|
47
|
-
: where[k][1],
|
|
48
|
-
};
|
|
49
|
-
break;
|
|
50
|
-
case 'LIKE': {
|
|
51
|
-
const first = where[k][1][0];
|
|
52
|
-
const last = where[k][1].slice(-1);
|
|
53
|
-
let reg;
|
|
54
|
-
if (first === '%' && last === '%') {
|
|
55
|
-
reg = new RegExp(where[k][1].slice(1, -1));
|
|
56
|
-
} else if (first === '%') {
|
|
57
|
-
reg = new RegExp(where[k][1].slice(1) + '$');
|
|
58
|
-
} else if (last === '%') {
|
|
59
|
-
reg = new RegExp('^' + where[k][1].slice(0, -1));
|
|
60
|
-
}
|
|
61
|
-
_where[parseKey(k)] = { $regex: reg };
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
case '!=':
|
|
65
|
-
_where[parseKey(k)] = { $ne: where[k] };
|
|
66
|
-
break;
|
|
67
|
-
case '>':
|
|
68
|
-
_where[parseKey(k)] = { $gt: where[k] };
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return _where;
|
|
76
|
-
}
|
|
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
|
-
|
|
99
|
-
async _select(where, { desc, limit, offset, field } = {}) {
|
|
100
|
-
const instance = this.db.table(this.tableName);
|
|
101
|
-
const query = instance.where(this.where(where));
|
|
102
|
-
|
|
103
|
-
if (desc) {
|
|
104
|
-
query.sort({ [desc]: -1 });
|
|
105
|
-
}
|
|
106
|
-
if (limit) {
|
|
107
|
-
query.limit(limit);
|
|
108
|
-
}
|
|
109
|
-
if (offset) {
|
|
110
|
-
query.skip(offset);
|
|
111
|
-
}
|
|
112
|
-
if (field) {
|
|
113
|
-
const _field = {};
|
|
114
|
-
field.forEach((f) => {
|
|
115
|
-
_field[f] = 1;
|
|
116
|
-
});
|
|
117
|
-
query.projection(_field);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const data = await query.find();
|
|
121
|
-
data.forEach((item) => {
|
|
122
|
-
item.objectId = item._id.toString();
|
|
123
|
-
delete item._id;
|
|
124
|
-
});
|
|
125
|
-
return data;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
async select(where, options = {}) {
|
|
129
|
-
let data = [];
|
|
130
|
-
let ret = [];
|
|
131
|
-
let offset = options.offset || 0;
|
|
132
|
-
do {
|
|
133
|
-
options.offset = offset + data.length;
|
|
134
|
-
ret = await this._select(where, options);
|
|
135
|
-
data = data.concat(ret);
|
|
136
|
-
} while (ret.length === 1000);
|
|
137
|
-
|
|
138
|
-
return data;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async count(where = {}) {
|
|
142
|
-
const instance = this.db.table(this.tableName);
|
|
143
|
-
const query = instance.where(this.where(where));
|
|
144
|
-
|
|
145
|
-
return query.count();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async add(data) {
|
|
149
|
-
const instance = this.db.table(this.tableName);
|
|
150
|
-
const tableData = instance.create(data);
|
|
151
|
-
await instance.save(tableData);
|
|
152
|
-
|
|
153
|
-
tableData.objectId = tableData._id.toString();
|
|
154
|
-
delete tableData._id;
|
|
155
|
-
return tableData;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async update(data, where) {
|
|
159
|
-
const instance = this.db.table(this.tableName);
|
|
160
|
-
const query = instance.where(this.where(where));
|
|
161
|
-
const items = await query.find();
|
|
162
|
-
|
|
163
|
-
return Promise.all(
|
|
164
|
-
items.map(async (item) => {
|
|
165
|
-
const updateData = typeof data === 'function' ? data(item) : data;
|
|
166
|
-
for (const k in updateData) {
|
|
167
|
-
item[k] = updateData[k];
|
|
168
|
-
}
|
|
169
|
-
await instance.save(item);
|
|
170
|
-
return item;
|
|
171
|
-
})
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async delete(where) {
|
|
176
|
-
const instance = this.db.table(this.tableName);
|
|
177
|
-
const query = instance.where(this.where(where));
|
|
178
|
-
return query.delete();
|
|
179
|
-
}
|
|
180
|
-
};
|