@waline/vercel 1.18.9 → 1.19.2
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/__tests__/katex.spec.js +2 -0
- package/index.js +2 -0
- package/package.json +8 -8
- package/src/config/adapter.js +2 -0
- package/src/config/config.js +1 -0
- package/src/config/extend.js +6 -4
- package/src/controller/article.js +1 -0
- package/src/controller/comment.js +85 -6
- package/src/controller/db.js +60 -2
- package/src/controller/oauth.js +14 -8
- package/src/controller/rest.js +1 -0
- package/src/controller/token/2fa.js +4 -0
- package/src/controller/token.js +4 -0
- package/src/controller/user/password.js +2 -0
- package/src/controller/user.js +3 -0
- package/src/controller/verification.js +3 -0
- package/src/extend/controller.js +4 -0
- package/src/extend/think.js +7 -0
- package/src/logic/base.js +6 -0
- package/src/logic/comment.js +5 -0
- package/src/logic/db.js +1 -0
- package/src/logic/token/2fa.js +1 -0
- package/src/logic/user.js +2 -0
- package/src/service/akismet.js +1 -0
- package/src/service/avatar.js +5 -0
- package/src/service/markdown/katex.js +2 -0
- package/src/service/markdown/mathCommon.js +5 -0
- package/src/service/markdown/mathjax.js +3 -0
- package/src/service/notify.js +109 -80
- package/src/service/storage/cloudbase.js +21 -0
- package/src/service/storage/deta.js +28 -0
- package/src/service/storage/github.js +77 -49
- package/src/service/storage/leancloud.js +42 -1
- package/src/service/storage/mongodb.js +17 -0
- package/src/service/storage/mysql.js +17 -1
- package/src/service/storage/postgresql.js +9 -1
- package/vanilla.js +1 -0
package/__tests__/katex.spec.js
CHANGED
|
@@ -48,6 +48,7 @@ describe('inline katex', () => {
|
|
|
48
48
|
it('Should render error msg when content is wrong', () => {
|
|
49
49
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
50
50
|
const originalWarn = global.console.warn;
|
|
51
|
+
|
|
51
52
|
global.console.warn = vi.fn();
|
|
52
53
|
|
|
53
54
|
expect(markdownItWithError.render('$\\fra{a}{b}$')).toEqual(
|
|
@@ -119,6 +120,7 @@ $$
|
|
|
119
120
|
it('Should render error msg when content is wrong', () => {
|
|
120
121
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
121
122
|
const originalWarn = global.console.warn;
|
|
123
|
+
|
|
122
124
|
global.console.warn = vi.fn();
|
|
123
125
|
expect(markdownItWithError.render('$$\\fra{a}{b}$$')).toMatch(
|
|
124
126
|
/<p class='katex-block katex-error' title='[\s\S]*?'>[\s\S]*?<\/p>/
|
package/index.js
CHANGED
|
@@ -16,6 +16,7 @@ module.exports = function (configParams = {}) {
|
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
const loader = new Loader(app.options);
|
|
19
|
+
|
|
19
20
|
loader.loadAll('worker');
|
|
20
21
|
|
|
21
22
|
return function (req, res) {
|
|
@@ -30,6 +31,7 @@ module.exports = function (configParams = {}) {
|
|
|
30
31
|
})
|
|
31
32
|
.then(() => {
|
|
32
33
|
const callback = think.app.callback();
|
|
34
|
+
|
|
33
35
|
return callback(req, res);
|
|
34
36
|
})
|
|
35
37
|
.then(() => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/vercel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.2",
|
|
4
4
|
"description": "vercel server for waline comment system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"waline",
|
|
@@ -19,31 +19,31 @@
|
|
|
19
19
|
"@koa/cors": "3.3.0",
|
|
20
20
|
"akismet": "2.0.7",
|
|
21
21
|
"deta": "1.1.0",
|
|
22
|
-
"dompurify": "2.3.
|
|
22
|
+
"dompurify": "2.3.10",
|
|
23
23
|
"dy-node-ip2region": "1.0.1",
|
|
24
24
|
"fast-csv": "4.3.6",
|
|
25
|
+
"form-data": "4.0.0",
|
|
25
26
|
"jsdom": "20.0.0",
|
|
26
27
|
"jsonwebtoken": "8.5.1",
|
|
27
28
|
"katex": "0.16.0",
|
|
28
|
-
"leancloud-storage": "4.
|
|
29
|
+
"leancloud-storage": "4.13.1",
|
|
29
30
|
"markdown-it": "13.0.1",
|
|
30
31
|
"markdown-it-emoji": "2.0.2",
|
|
31
32
|
"markdown-it-sub": "1.0.0",
|
|
32
33
|
"markdown-it-sup": "1.0.0",
|
|
33
34
|
"mathjax-full": "3.2.2",
|
|
34
|
-
"
|
|
35
|
+
"node-fetch": "2.6.7",
|
|
36
|
+
"nodemailer": "6.7.8",
|
|
35
37
|
"nunjucks": "3.2.3",
|
|
36
38
|
"phpass": "0.1.1",
|
|
37
39
|
"prismjs": "1.28.0",
|
|
38
|
-
"request": "2.88.2",
|
|
39
|
-
"request-promise-native": "1.0.9",
|
|
40
40
|
"speakeasy": "2.0.0",
|
|
41
|
-
"think-helper": "
|
|
41
|
+
"think-helper": "1.1.3",
|
|
42
42
|
"think-logger3": "1.3.1",
|
|
43
43
|
"think-model": "1.5.4",
|
|
44
44
|
"think-model-mysql": "1.1.7",
|
|
45
45
|
"think-model-postgresql": "1.1.7",
|
|
46
|
-
"think-model-sqlite": "1.
|
|
46
|
+
"think-model-sqlite": "1.3.0",
|
|
47
47
|
"think-mongo": "2.2.1",
|
|
48
48
|
"think-router-rest": "1.0.5",
|
|
49
49
|
"thinkjs": "3.2.14",
|
package/src/config/adapter.js
CHANGED
|
@@ -40,6 +40,7 @@ const {
|
|
|
40
40
|
|
|
41
41
|
let type = 'common';
|
|
42
42
|
const mongoOpt = {};
|
|
43
|
+
|
|
43
44
|
if (MONGO_REPLICASET) mongoOpt.replicaSet = MONGO_REPLICASET;
|
|
44
45
|
if (MONGO_AUTHSOURCE) mongoOpt.authSource = MONGO_AUTHSOURCE;
|
|
45
46
|
|
|
@@ -51,6 +52,7 @@ if (MONGO_DB) {
|
|
|
51
52
|
.slice(10)
|
|
52
53
|
.toLocaleLowerCase()
|
|
53
54
|
.replace(/_([a-z])/g, (_, b) => b.toUpperCase());
|
|
55
|
+
|
|
54
56
|
mongoOpt[key] = process.env[envKeys];
|
|
55
57
|
}
|
|
56
58
|
}
|
package/src/config/config.js
CHANGED
package/src/config/extend.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
const fetch = require('node-fetch');
|
|
1
2
|
const Model = require('think-model');
|
|
2
3
|
const Mongo = require('think-mongo');
|
|
3
|
-
const request = require('request-promise-native');
|
|
4
4
|
|
|
5
5
|
module.exports = [
|
|
6
6
|
Model(think.app),
|
|
@@ -9,27 +9,29 @@ module.exports = [
|
|
|
9
9
|
context: {
|
|
10
10
|
get serverURL() {
|
|
11
11
|
const { SERVER_URL } = process.env;
|
|
12
|
+
|
|
12
13
|
if (SERVER_URL) {
|
|
13
14
|
return SERVER_URL;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
const { protocol, host } = this;
|
|
18
|
+
|
|
17
19
|
return `${protocol}://${host}`;
|
|
18
20
|
},
|
|
19
21
|
async webhook(type, data) {
|
|
20
22
|
const { WEBHOOK } = process.env;
|
|
23
|
+
|
|
21
24
|
if (!WEBHOOK) {
|
|
22
25
|
return;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
return
|
|
26
|
-
uri: WEBHOOK,
|
|
28
|
+
return fetch(WEBHOOK, {
|
|
27
29
|
method: 'POST',
|
|
28
30
|
headers: {
|
|
29
31
|
'content-type': 'application/json',
|
|
30
32
|
},
|
|
31
33
|
body: JSON.stringify({ type, data }),
|
|
32
|
-
});
|
|
34
|
+
}).then((resp) => resp.json());
|
|
33
35
|
},
|
|
34
36
|
},
|
|
35
37
|
},
|
|
@@ -4,6 +4,7 @@ const akismet = require('../service/akismet');
|
|
|
4
4
|
const { getMarkdownParser } = require('../service/markdown');
|
|
5
5
|
|
|
6
6
|
const markdownParser = getMarkdownParser();
|
|
7
|
+
|
|
7
8
|
async function formatCmt(
|
|
8
9
|
{ ua, user_id, ip, ...comment },
|
|
9
10
|
users = [],
|
|
@@ -20,6 +21,7 @@ async function formatCmt(
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
const user = users.find(({ objectId }) => user_id === objectId);
|
|
24
|
+
|
|
23
25
|
if (!think.isEmpty(user)) {
|
|
24
26
|
comment.nick = user.display_name;
|
|
25
27
|
comment.mail = user.email;
|
|
@@ -32,12 +34,14 @@ async function formatCmt(
|
|
|
32
34
|
user && user.avatar
|
|
33
35
|
? user.avatar
|
|
34
36
|
: await think.service('avatar').stringify(comment);
|
|
37
|
+
|
|
35
38
|
comment.avatar =
|
|
36
39
|
avatarProxy && !avatarUrl.includes(avatarProxy)
|
|
37
40
|
? avatarProxy + '?url=' + encodeURIComponent(avatarUrl)
|
|
38
41
|
: avatarUrl;
|
|
39
42
|
|
|
40
43
|
const isAdmin = loginUser && loginUser.type === 'administrator';
|
|
44
|
+
|
|
41
45
|
if (!isAdmin) {
|
|
42
46
|
delete comment.mail;
|
|
43
47
|
} else {
|
|
@@ -51,6 +55,7 @@ async function formatCmt(
|
|
|
51
55
|
}
|
|
52
56
|
comment.comment = markdownParser(comment.comment);
|
|
53
57
|
comment.like = Number(comment.like) || 0;
|
|
58
|
+
|
|
54
59
|
return comment;
|
|
55
60
|
}
|
|
56
61
|
|
|
@@ -71,6 +76,7 @@ module.exports = class extends BaseRest {
|
|
|
71
76
|
case 'recent': {
|
|
72
77
|
const { count } = this.get();
|
|
73
78
|
const where = {};
|
|
79
|
+
|
|
74
80
|
if (think.isEmpty(userInfo) || this.config('storage') === 'deta') {
|
|
75
81
|
where.status = ['NOT IN', ['waiting', 'spam']];
|
|
76
82
|
} else {
|
|
@@ -111,6 +117,7 @@ module.exports = class extends BaseRest {
|
|
|
111
117
|
);
|
|
112
118
|
|
|
113
119
|
let users = [];
|
|
120
|
+
|
|
114
121
|
if (user_ids.length) {
|
|
115
122
|
users = await userModel.select(
|
|
116
123
|
{ objectId: ['IN', user_ids] },
|
|
@@ -139,6 +146,7 @@ module.exports = class extends BaseRest {
|
|
|
139
146
|
case 'count': {
|
|
140
147
|
const { url } = this.get();
|
|
141
148
|
const where = { url: ['IN', url] };
|
|
149
|
+
|
|
142
150
|
if (think.isEmpty(userInfo) || this.config('storage') === 'deta') {
|
|
143
151
|
where.status = ['NOT IN', ['waiting', 'spam']];
|
|
144
152
|
} else {
|
|
@@ -162,6 +170,7 @@ module.exports = class extends BaseRest {
|
|
|
162
170
|
|
|
163
171
|
if (owner === 'mine') {
|
|
164
172
|
const { userInfo } = this.ctx.state;
|
|
173
|
+
|
|
165
174
|
where.mail = userInfo.email;
|
|
166
175
|
}
|
|
167
176
|
|
|
@@ -198,6 +207,7 @@ module.exports = class extends BaseRest {
|
|
|
198
207
|
);
|
|
199
208
|
|
|
200
209
|
let users = [];
|
|
210
|
+
|
|
201
211
|
if (user_ids.length) {
|
|
202
212
|
users = await userModel.select(
|
|
203
213
|
{ objectId: ['IN', user_ids] },
|
|
@@ -231,6 +241,7 @@ module.exports = class extends BaseRest {
|
|
|
231
241
|
default: {
|
|
232
242
|
const { path: url, page, pageSize } = this.get();
|
|
233
243
|
const where = { url };
|
|
244
|
+
|
|
234
245
|
if (think.isEmpty(userInfo) || this.config('storage') === 'deta') {
|
|
235
246
|
where.status = ['NOT IN', ['waiting', 'spam']];
|
|
236
247
|
} else if (userInfo.type !== 'administrator') {
|
|
@@ -290,6 +301,7 @@ module.exports = class extends BaseRest {
|
|
|
290
301
|
...comments.filter(({ rid, sticky }) => !rid && !sticky),
|
|
291
302
|
].slice(pageOffset, pageOffset + pageSize);
|
|
292
303
|
const rootIds = {};
|
|
304
|
+
|
|
293
305
|
rootComments.forEach(({ objectId }) => {
|
|
294
306
|
rootIds[objectId] = true;
|
|
295
307
|
});
|
|
@@ -312,6 +324,7 @@ module.exports = class extends BaseRest {
|
|
|
312
324
|
},
|
|
313
325
|
selectOptions
|
|
314
326
|
);
|
|
327
|
+
|
|
315
328
|
comments = [...rootComments, ...children];
|
|
316
329
|
rootCount = await this.modelInstance.count({
|
|
317
330
|
...where,
|
|
@@ -349,12 +362,14 @@ module.exports = class extends BaseRest {
|
|
|
349
362
|
status: ['NOT IN', ['waiting', 'spam']],
|
|
350
363
|
_complex: {},
|
|
351
364
|
};
|
|
365
|
+
|
|
352
366
|
if (user_ids.length) {
|
|
353
367
|
countWhere._complex.user_id = ['IN', user_ids];
|
|
354
368
|
}
|
|
355
369
|
const mails = Array.from(
|
|
356
370
|
new Set(comments.map(({ mail }) => mail).filter((v) => v))
|
|
357
371
|
);
|
|
372
|
+
|
|
358
373
|
if (mails.length) {
|
|
359
374
|
countWhere._complex.mail = ['IN', mails];
|
|
360
375
|
}
|
|
@@ -366,20 +381,24 @@ module.exports = class extends BaseRest {
|
|
|
366
381
|
const counts = await this.modelInstance.count(countWhere, {
|
|
367
382
|
group: ['user_id', 'mail'],
|
|
368
383
|
});
|
|
384
|
+
|
|
369
385
|
comments.forEach((cmt) => {
|
|
370
386
|
const countItem = (counts || []).find(({ mail, user_id }) => {
|
|
371
387
|
if (cmt.user_id) {
|
|
372
388
|
return user_id === cmt.user_id;
|
|
373
389
|
}
|
|
390
|
+
|
|
374
391
|
return mail === cmt.mail;
|
|
375
392
|
});
|
|
376
393
|
|
|
377
394
|
let level = 0;
|
|
395
|
+
|
|
378
396
|
if (countItem) {
|
|
379
397
|
const _level = think.findLastIndex(
|
|
380
398
|
this.config('levels'),
|
|
381
399
|
(l) => l <= countItem.count
|
|
382
400
|
);
|
|
401
|
+
|
|
383
402
|
if (_level !== -1) {
|
|
384
403
|
level = _level;
|
|
385
404
|
}
|
|
@@ -401,12 +420,14 @@ module.exports = class extends BaseRest {
|
|
|
401
420
|
this.config(),
|
|
402
421
|
userInfo
|
|
403
422
|
);
|
|
423
|
+
|
|
404
424
|
cmt.children = await Promise.all(
|
|
405
425
|
comments
|
|
406
426
|
.filter(({ rid }) => rid === cmt.objectId)
|
|
407
427
|
.map((cmt) => formatCmt(cmt, users, this.config(), userInfo))
|
|
408
428
|
.reverse()
|
|
409
429
|
);
|
|
430
|
+
|
|
410
431
|
return cmt;
|
|
411
432
|
})
|
|
412
433
|
),
|
|
@@ -486,6 +507,7 @@ module.exports = class extends BaseRest {
|
|
|
486
507
|
|
|
487
508
|
if (!think.isEmpty(recent)) {
|
|
488
509
|
think.logger.debug(`The author has posted in ${IPQPS} seconeds.`);
|
|
510
|
+
|
|
489
511
|
return this.fail(this.locale('Comment too fast!'));
|
|
490
512
|
}
|
|
491
513
|
|
|
@@ -520,6 +542,7 @@ module.exports = class extends BaseRest {
|
|
|
520
542
|
|
|
521
543
|
if (!think.isEmpty(forbiddenWords)) {
|
|
522
544
|
const regexp = new RegExp('(' + forbiddenWords.join('|') + ')', 'ig');
|
|
545
|
+
|
|
523
546
|
if (regexp.test(comment)) {
|
|
524
547
|
data.status = 'spam';
|
|
525
548
|
}
|
|
@@ -544,9 +567,17 @@ module.exports = class extends BaseRest {
|
|
|
544
567
|
think.logger.debug(`Comment have been added to storage.`);
|
|
545
568
|
|
|
546
569
|
let parentComment;
|
|
570
|
+
let parentUser;
|
|
571
|
+
|
|
547
572
|
if (pid) {
|
|
548
573
|
parentComment = await this.modelInstance.select({ objectId: pid });
|
|
549
574
|
parentComment = parentComment[0];
|
|
575
|
+
if (parentComment.user_id) {
|
|
576
|
+
parentUser = await this.model('User').select({
|
|
577
|
+
objectId: parentComment.user_id,
|
|
578
|
+
});
|
|
579
|
+
parentUser = parentUser[0];
|
|
580
|
+
}
|
|
550
581
|
}
|
|
551
582
|
|
|
552
583
|
await this.ctx.webhook('new_comment', {
|
|
@@ -554,13 +585,27 @@ module.exports = class extends BaseRest {
|
|
|
554
585
|
reply: parentComment,
|
|
555
586
|
});
|
|
556
587
|
|
|
588
|
+
const cmtReturn = await formatCmt(
|
|
589
|
+
resp,
|
|
590
|
+
[userInfo],
|
|
591
|
+
this.config(),
|
|
592
|
+
userInfo
|
|
593
|
+
);
|
|
594
|
+
const parentReturn = parentComment
|
|
595
|
+
? await formatCmt(
|
|
596
|
+
parentComment,
|
|
597
|
+
parentUser ? [parentUser] : [],
|
|
598
|
+
this.config(),
|
|
599
|
+
userInfo
|
|
600
|
+
)
|
|
601
|
+
: undefined;
|
|
602
|
+
|
|
557
603
|
if (comment.status !== 'spam') {
|
|
558
604
|
const notify = this.service('notify');
|
|
605
|
+
|
|
559
606
|
await notify.run(
|
|
560
|
-
{ ...
|
|
561
|
-
parentComment
|
|
562
|
-
? { ...parentComment, comment: markdownParser(parentComment.comment) }
|
|
563
|
-
: undefined
|
|
607
|
+
{ ...cmtReturn, mail: resp.mail, rawComment: comment },
|
|
608
|
+
parentReturn ? { ...parentReturn, mail: parentComment.mail } : undefined
|
|
564
609
|
);
|
|
565
610
|
}
|
|
566
611
|
|
|
@@ -579,6 +624,7 @@ module.exports = class extends BaseRest {
|
|
|
579
624
|
const { userInfo } = this.ctx.state;
|
|
580
625
|
let data = this.post();
|
|
581
626
|
let oldData = await this.modelInstance.select({ objectId: this.id });
|
|
627
|
+
|
|
582
628
|
if (think.isEmpty(oldData)) {
|
|
583
629
|
return this.success();
|
|
584
630
|
}
|
|
@@ -590,6 +636,7 @@ module.exports = class extends BaseRest {
|
|
|
590
636
|
}
|
|
591
637
|
|
|
592
638
|
const likeIncMax = this.config('LIKE_INC_MAX') || 1;
|
|
639
|
+
|
|
593
640
|
data = {
|
|
594
641
|
like:
|
|
595
642
|
(Number(oldData.like) || 0) +
|
|
@@ -615,15 +662,47 @@ module.exports = class extends BaseRest {
|
|
|
615
662
|
data.status === 'approved' &&
|
|
616
663
|
oldData.pid
|
|
617
664
|
) {
|
|
665
|
+
let cmtUser;
|
|
666
|
+
|
|
667
|
+
if (newData.user_id) {
|
|
668
|
+
cmtUser = await this.model('User').select({
|
|
669
|
+
objectId: newData.user_id,
|
|
670
|
+
});
|
|
671
|
+
cmtUser = cmtUser[0];
|
|
672
|
+
}
|
|
673
|
+
|
|
618
674
|
let pComment = await this.modelInstance.select({
|
|
619
675
|
objectId: oldData.pid,
|
|
620
676
|
});
|
|
677
|
+
|
|
621
678
|
pComment = pComment[0];
|
|
622
679
|
|
|
680
|
+
let pUser;
|
|
681
|
+
|
|
682
|
+
if (pComment.user_id) {
|
|
683
|
+
pUser = await this.model('User').select({
|
|
684
|
+
objectId: pComment.user_id,
|
|
685
|
+
});
|
|
686
|
+
pUser = pUser[0];
|
|
687
|
+
}
|
|
688
|
+
|
|
623
689
|
const notify = this.service('notify');
|
|
690
|
+
const cmtReturn = await formatCmt(
|
|
691
|
+
newData,
|
|
692
|
+
cmtUser ? [cmtUser] : [],
|
|
693
|
+
this.config(),
|
|
694
|
+
userInfo
|
|
695
|
+
);
|
|
696
|
+
const pcmtReturn = await formatCmt(
|
|
697
|
+
pComment,
|
|
698
|
+
pUser ? [pUser] : [],
|
|
699
|
+
this.config(),
|
|
700
|
+
userInfo
|
|
701
|
+
);
|
|
702
|
+
|
|
624
703
|
await notify.run(
|
|
625
|
-
{ ...
|
|
626
|
-
{ ...
|
|
704
|
+
{ ...cmtReturn, mail: newData.mail },
|
|
705
|
+
{ ...pcmtReturn, mail: pComment.mail },
|
|
627
706
|
true
|
|
628
707
|
);
|
|
629
708
|
}
|
package/src/controller/db.js
CHANGED
|
@@ -6,8 +6,10 @@ const readFileAsync = util.promisify(fs.readFile);
|
|
|
6
6
|
|
|
7
7
|
function formatID(data, idGenerator) {
|
|
8
8
|
const objectIdMap = {};
|
|
9
|
+
|
|
9
10
|
for (let i = 0; i < data.length; i++) {
|
|
10
11
|
const { objectId } = data[i];
|
|
12
|
+
|
|
11
13
|
objectIdMap[objectId] = idGenerator(data[i], i, data);
|
|
12
14
|
}
|
|
13
15
|
|
|
@@ -21,6 +23,7 @@ function formatID(data, idGenerator) {
|
|
|
21
23
|
|
|
22
24
|
return data;
|
|
23
25
|
}
|
|
26
|
+
|
|
24
27
|
module.exports = class extends BaseRest {
|
|
25
28
|
async getAction() {
|
|
26
29
|
const exportData = {
|
|
@@ -41,6 +44,7 @@ module.exports = class extends BaseRest {
|
|
|
41
44
|
const model = this.service(`storage/${storage}`, tableName);
|
|
42
45
|
|
|
43
46
|
const data = await model.select({});
|
|
47
|
+
|
|
44
48
|
exportData.data[tableName] = data;
|
|
45
49
|
}
|
|
46
50
|
|
|
@@ -49,36 +53,90 @@ module.exports = class extends BaseRest {
|
|
|
49
53
|
|
|
50
54
|
async postAction() {
|
|
51
55
|
const file = this.file('file');
|
|
56
|
+
|
|
52
57
|
try {
|
|
53
58
|
const jsonText = await readFileAsync(file.path, 'utf-8');
|
|
54
59
|
const importData = JSON.parse(jsonText);
|
|
60
|
+
|
|
55
61
|
if (!importData || importData.type !== 'waline') {
|
|
56
62
|
return this.fail(this.locale('import data format not support!'));
|
|
57
63
|
}
|
|
58
64
|
|
|
65
|
+
const idMaps = {};
|
|
66
|
+
const storage = this.config('storage');
|
|
67
|
+
|
|
59
68
|
for (let i = 0; i < importData.tables.length; i++) {
|
|
60
69
|
const tableName = importData.tables[i];
|
|
61
|
-
const storage = this.config('storage');
|
|
62
70
|
const model = this.service(`storage/${storage}`, tableName);
|
|
63
71
|
|
|
72
|
+
idMaps[tableName] = new Map();
|
|
64
73
|
let data = importData.data[tableName];
|
|
74
|
+
|
|
65
75
|
if (['postgresql', 'mysql', 'sqlite'].includes(storage)) {
|
|
66
76
|
let i = 0;
|
|
77
|
+
|
|
67
78
|
data = formatID(data, () => (i = i + 1));
|
|
79
|
+
} else if (storage === 'leancloud') {
|
|
80
|
+
data
|
|
81
|
+
.filter(({ insertedAt }) => insertedAt)
|
|
82
|
+
.forEach((item) => {
|
|
83
|
+
item.insertedAt = new Date(item.insertedAt);
|
|
84
|
+
});
|
|
68
85
|
}
|
|
69
86
|
|
|
70
87
|
// delete all data at first
|
|
71
88
|
await model.delete({});
|
|
72
89
|
// then add data one by one
|
|
73
90
|
for (let j = 0; j < data.length; j++) {
|
|
74
|
-
await model.add(data[j]);
|
|
91
|
+
const ret = await model.add(data[j]);
|
|
92
|
+
|
|
93
|
+
idMaps[tableName].set(data[j].objectId, ret.objectId);
|
|
75
94
|
}
|
|
76
95
|
}
|
|
96
|
+
|
|
97
|
+
const cmtModel = this.service(`storage/${storage}`, 'Comment');
|
|
98
|
+
const commentData = importData.data.Comment;
|
|
99
|
+
const willUpdateData = [];
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < commentData.length; i++) {
|
|
102
|
+
const cmt = commentData[i];
|
|
103
|
+
const willUpdateItem = {};
|
|
104
|
+
|
|
105
|
+
[
|
|
106
|
+
{ tableName: 'Comment', field: 'pid' },
|
|
107
|
+
{ tableName: 'Comment', field: 'rid' },
|
|
108
|
+
{ tableName: 'Users', field: 'user_id' },
|
|
109
|
+
].forEach(({ tableName, field }) => {
|
|
110
|
+
if (!cmt[field]) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const oldId = cmt[field];
|
|
114
|
+
const newId = idMaps[tableName].get(cmt[field]);
|
|
115
|
+
|
|
116
|
+
if (oldId !== newId) {
|
|
117
|
+
willUpdateItem[field] = newId;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
if (!think.isEmpty(willUpdateItem)) {
|
|
121
|
+
willUpdateData.push([
|
|
122
|
+
willUpdateItem,
|
|
123
|
+
{ objectId: idMaps.Comment.get(cmt.objectId) },
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (let i = 0; i < willUpdateData.length; i++) {
|
|
128
|
+
const [data, where] = willUpdateData[i];
|
|
129
|
+
|
|
130
|
+
await cmtModel.update(data, where);
|
|
131
|
+
}
|
|
132
|
+
|
|
77
133
|
return this.success();
|
|
78
134
|
} catch (e) {
|
|
79
135
|
if (think.isPrevent(e)) {
|
|
80
136
|
return this.success();
|
|
81
137
|
}
|
|
138
|
+
console.log(e);
|
|
139
|
+
|
|
82
140
|
return this.fail(e.message);
|
|
83
141
|
}
|
|
84
142
|
}
|
package/src/controller/oauth.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
const qs = require('querystring');
|
|
2
1
|
const jwt = require('jsonwebtoken');
|
|
2
|
+
const fetch = require('node-fetch');
|
|
3
3
|
const { PasswordHash } = require('phpass');
|
|
4
|
-
const
|
|
4
|
+
const qs = require('querystring');
|
|
5
|
+
|
|
5
6
|
module.exports = class extends think.Controller {
|
|
6
7
|
constructor(ctx) {
|
|
7
8
|
super(ctx);
|
|
@@ -17,12 +18,14 @@ module.exports = class extends think.Controller {
|
|
|
17
18
|
|
|
18
19
|
const hasCode =
|
|
19
20
|
type === 'twitter' ? oauth_token && oauth_verifier : Boolean(code);
|
|
21
|
+
|
|
20
22
|
if (!hasCode) {
|
|
21
23
|
const { serverURL } = this.ctx;
|
|
22
24
|
const redirectUrl = `${serverURL}/oauth?${qs.stringify({
|
|
23
25
|
redirect,
|
|
24
26
|
type,
|
|
25
27
|
})}`;
|
|
28
|
+
|
|
26
29
|
return this.redirect(
|
|
27
30
|
`${oauthUrl}/${type}?${qs.stringify({
|
|
28
31
|
redirect: redirectUrl,
|
|
@@ -35,28 +38,29 @@ module.exports = class extends think.Controller {
|
|
|
35
38
|
* user = { id, name, email, avatar,url };
|
|
36
39
|
*/
|
|
37
40
|
const params = { code, oauth_verifier, oauth_token };
|
|
41
|
+
|
|
38
42
|
if (type === 'facebook') {
|
|
39
43
|
const { serverURL } = this.ctx;
|
|
40
44
|
const redirectUrl = `${serverURL}/oauth?${qs.stringify({
|
|
41
45
|
redirect,
|
|
42
46
|
type,
|
|
43
47
|
})}`;
|
|
48
|
+
|
|
44
49
|
params.state = qs.stringify({
|
|
45
50
|
redirect: redirectUrl,
|
|
46
51
|
state: this.ctx.state.token || '',
|
|
47
52
|
});
|
|
48
53
|
}
|
|
49
54
|
|
|
50
|
-
const user = await
|
|
51
|
-
url: `${oauthUrl}/${type}?${qs.stringify(params)}`,
|
|
55
|
+
const user = await fetch(`${oauthUrl}/${type}?${qs.stringify(params)}`, {
|
|
52
56
|
method: 'GET',
|
|
53
|
-
json: true,
|
|
54
57
|
headers: {
|
|
55
|
-
'
|
|
58
|
+
'user-agent': '@waline',
|
|
56
59
|
},
|
|
57
|
-
});
|
|
60
|
+
}).then((resp) => resp.json());
|
|
61
|
+
|
|
58
62
|
if (!user || !user.id) {
|
|
59
|
-
return this.fail();
|
|
63
|
+
return this.fail(user);
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
const userBySocial = await this.modelInstance.select({ [type]: user.id });
|
|
@@ -78,6 +82,7 @@ module.exports = class extends think.Controller {
|
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
const current = this.ctx.state.userInfo;
|
|
85
|
+
|
|
81
86
|
if (!think.isEmpty(current)) {
|
|
82
87
|
const updateData = { [type]: user.id };
|
|
83
88
|
|
|
@@ -93,6 +98,7 @@ module.exports = class extends think.Controller {
|
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
const userByEmail = await this.modelInstance.select({ email: user.email });
|
|
101
|
+
|
|
96
102
|
if (think.isEmpty(userByEmail)) {
|
|
97
103
|
const count = await this.modelInstance.count();
|
|
98
104
|
const data = {
|
package/src/controller/rest.js
CHANGED
|
@@ -19,10 +19,12 @@ module.exports = class extends BaseRest {
|
|
|
19
19
|
}
|
|
20
20
|
);
|
|
21
21
|
const is2FAEnabled = !think.isEmpty(user) && Boolean(user[0]['2fa']);
|
|
22
|
+
|
|
22
23
|
return this.success({ enable: is2FAEnabled });
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
const name = `waline_${userInfo.objectId}`;
|
|
27
|
+
|
|
26
28
|
if (userInfo['2fa'] && userInfo['2fa'].length == 32) {
|
|
27
29
|
return this.success({
|
|
28
30
|
otpauth_url: `otpauth://totp/${name}?secret=${userInfo['2fa']}`,
|
|
@@ -34,6 +36,7 @@ module.exports = class extends BaseRest {
|
|
|
34
36
|
length: 20,
|
|
35
37
|
name,
|
|
36
38
|
});
|
|
39
|
+
|
|
37
40
|
return this.success({ otpauth_url, secret });
|
|
38
41
|
}
|
|
39
42
|
|
|
@@ -55,6 +58,7 @@ module.exports = class extends BaseRest {
|
|
|
55
58
|
'Users'
|
|
56
59
|
);
|
|
57
60
|
const { objectId } = this.ctx.state.userInfo;
|
|
61
|
+
|
|
58
62
|
await userModel.update({ ['2fa']: data.secret }, { objectId });
|
|
59
63
|
|
|
60
64
|
return this.success();
|