@waline/vercel 1.18.9 → 1.19.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/__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 +29 -0
- package/src/controller/db.js +54 -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 +11 -0
- 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.0",
|
|
4
4
|
"description": "vercel server for waline comment system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"waline",
|
|
@@ -19,26 +19,25 @@
|
|
|
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
|
-
"nodemailer": "6.7.
|
|
35
|
+
"nodemailer": "6.7.7",
|
|
35
36
|
"nunjucks": "3.2.3",
|
|
36
37
|
"phpass": "0.1.1",
|
|
37
38
|
"prismjs": "1.28.0",
|
|
38
|
-
"request": "2.88.2",
|
|
39
|
-
"request-promise-native": "1.0.9",
|
|
40
39
|
"speakeasy": "2.0.0",
|
|
41
|
-
"think-helper": "
|
|
40
|
+
"think-helper": "1.1.3",
|
|
42
41
|
"think-logger3": "1.3.1",
|
|
43
42
|
"think-model": "1.5.4",
|
|
44
43
|
"think-model-mysql": "1.1.7",
|
|
@@ -47,7 +46,8 @@
|
|
|
47
46
|
"think-mongo": "2.2.1",
|
|
48
47
|
"think-router-rest": "1.0.5",
|
|
49
48
|
"thinkjs": "3.2.14",
|
|
50
|
-
"ua-parser-js": "1.0.2"
|
|
49
|
+
"ua-parser-js": "1.0.2",
|
|
50
|
+
"undici": "^5.8.0"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">=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('undici');
|
|
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,6 +567,7 @@ module.exports = class extends BaseRest {
|
|
|
544
567
|
think.logger.debug(`Comment have been added to storage.`);
|
|
545
568
|
|
|
546
569
|
let parentComment;
|
|
570
|
+
|
|
547
571
|
if (pid) {
|
|
548
572
|
parentComment = await this.modelInstance.select({ objectId: pid });
|
|
549
573
|
parentComment = parentComment[0];
|
|
@@ -556,6 +580,7 @@ module.exports = class extends BaseRest {
|
|
|
556
580
|
|
|
557
581
|
if (comment.status !== 'spam') {
|
|
558
582
|
const notify = this.service('notify');
|
|
583
|
+
|
|
559
584
|
await notify.run(
|
|
560
585
|
{ ...resp, comment: markdownParser(resp.comment), rawComment: comment },
|
|
561
586
|
parentComment
|
|
@@ -579,6 +604,7 @@ module.exports = class extends BaseRest {
|
|
|
579
604
|
const { userInfo } = this.ctx.state;
|
|
580
605
|
let data = this.post();
|
|
581
606
|
let oldData = await this.modelInstance.select({ objectId: this.id });
|
|
607
|
+
|
|
582
608
|
if (think.isEmpty(oldData)) {
|
|
583
609
|
return this.success();
|
|
584
610
|
}
|
|
@@ -590,6 +616,7 @@ module.exports = class extends BaseRest {
|
|
|
590
616
|
}
|
|
591
617
|
|
|
592
618
|
const likeIncMax = this.config('LIKE_INC_MAX') || 1;
|
|
619
|
+
|
|
593
620
|
data = {
|
|
594
621
|
like:
|
|
595
622
|
(Number(oldData.like) || 0) +
|
|
@@ -618,9 +645,11 @@ module.exports = class extends BaseRest {
|
|
|
618
645
|
let pComment = await this.modelInstance.select({
|
|
619
646
|
objectId: oldData.pid,
|
|
620
647
|
});
|
|
648
|
+
|
|
621
649
|
pComment = pComment[0];
|
|
622
650
|
|
|
623
651
|
const notify = this.service('notify');
|
|
652
|
+
|
|
624
653
|
await notify.run(
|
|
625
654
|
{ ...newData, comment: markdownParser(newData.comment) },
|
|
626
655
|
{ ...pComment, comment: markdownParser(pComment.comment) },
|
package/src/controller/db.js
CHANGED
|
@@ -21,6 +21,7 @@ function formatID(data, idGenerator) {
|
|
|
21
21
|
|
|
22
22
|
return data;
|
|
23
23
|
}
|
|
24
|
+
|
|
24
25
|
module.exports = class extends BaseRest {
|
|
25
26
|
async getAction() {
|
|
26
27
|
const exportData = {
|
|
@@ -49,36 +50,87 @@ module.exports = class extends BaseRest {
|
|
|
49
50
|
|
|
50
51
|
async postAction() {
|
|
51
52
|
const file = this.file('file');
|
|
53
|
+
|
|
52
54
|
try {
|
|
53
55
|
const jsonText = await readFileAsync(file.path, 'utf-8');
|
|
54
56
|
const importData = JSON.parse(jsonText);
|
|
57
|
+
|
|
55
58
|
if (!importData || importData.type !== 'waline') {
|
|
56
59
|
return this.fail(this.locale('import data format not support!'));
|
|
57
60
|
}
|
|
58
61
|
|
|
62
|
+
const idMaps = {};
|
|
63
|
+
const storage = this.config('storage');
|
|
64
|
+
|
|
59
65
|
for (let i = 0; i < importData.tables.length; i++) {
|
|
60
66
|
const tableName = importData.tables[i];
|
|
61
|
-
const storage = this.config('storage');
|
|
62
67
|
const model = this.service(`storage/${storage}`, tableName);
|
|
63
68
|
|
|
69
|
+
idMaps[tableName] = new Map();
|
|
64
70
|
let data = importData.data[tableName];
|
|
65
71
|
if (['postgresql', 'mysql', 'sqlite'].includes(storage)) {
|
|
66
72
|
let i = 0;
|
|
67
73
|
data = formatID(data, () => (i = i + 1));
|
|
74
|
+
} else if (storage === 'leancloud') {
|
|
75
|
+
data
|
|
76
|
+
.filter(({ insertedAt }) => insertedAt)
|
|
77
|
+
.forEach((item) => {
|
|
78
|
+
item.insertedAt = new Date(item.insertedAt);
|
|
79
|
+
});
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
// delete all data at first
|
|
71
83
|
await model.delete({});
|
|
72
84
|
// then add data one by one
|
|
73
85
|
for (let j = 0; j < data.length; j++) {
|
|
74
|
-
await model.add(data[j]);
|
|
86
|
+
const ret = await model.add(data[j]);
|
|
87
|
+
|
|
88
|
+
idMaps[tableName].set(data[j].objectId, ret.objectId);
|
|
75
89
|
}
|
|
76
90
|
}
|
|
91
|
+
|
|
92
|
+
const cmtModel = this.service(`storage/${storage}`, 'Comment');
|
|
93
|
+
const commentData = importData.data.Comment;
|
|
94
|
+
const willUpdateData = [];
|
|
95
|
+
|
|
96
|
+
for (let i = 0; i < commentData.length; i++) {
|
|
97
|
+
const cmt = commentData[i];
|
|
98
|
+
const willUpdateItem = {};
|
|
99
|
+
|
|
100
|
+
[
|
|
101
|
+
{ tableName: 'Comment', field: 'pid' },
|
|
102
|
+
{ tableName: 'Comment', field: 'rid' },
|
|
103
|
+
{ tableName: 'Users', field: 'user_id' },
|
|
104
|
+
].forEach(({ tableName, field }) => {
|
|
105
|
+
if (!cmt[field]) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const oldId = cmt[field];
|
|
109
|
+
const newId = idMaps[tableName].get(cmt[field]);
|
|
110
|
+
|
|
111
|
+
if (oldId !== newId) {
|
|
112
|
+
willUpdateItem[field] = newId;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
if (!think.isEmpty(willUpdateItem)) {
|
|
116
|
+
willUpdateData.push([
|
|
117
|
+
willUpdateItem,
|
|
118
|
+
{ objectId: idMaps.Comment.get(cmt.objectId) },
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (let i = 0; i < willUpdateData.length; i++) {
|
|
123
|
+
const [data, where] = willUpdateData[i];
|
|
124
|
+
|
|
125
|
+
await cmtModel.update(data, where);
|
|
126
|
+
}
|
|
127
|
+
|
|
77
128
|
return this.success();
|
|
78
129
|
} catch (e) {
|
|
79
130
|
if (think.isPrevent(e)) {
|
|
80
131
|
return this.success();
|
|
81
132
|
}
|
|
133
|
+
console.log(e);
|
|
82
134
|
return this.fail(e.message);
|
|
83
135
|
}
|
|
84
136
|
}
|
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('undici');
|
|
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();
|
package/src/controller/token.js
CHANGED
|
@@ -35,6 +35,7 @@ module.exports = class extends BaseRest {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const twoFactorAuthSecret = user[0]['2fa'];
|
|
38
|
+
|
|
38
39
|
if (twoFactorAuthSecret) {
|
|
39
40
|
const verified = speakeasy.totp.verify({
|
|
40
41
|
secret: twoFactorAuthSecret,
|
|
@@ -42,6 +43,7 @@ module.exports = class extends BaseRest {
|
|
|
42
43
|
token: code,
|
|
43
44
|
window: 2,
|
|
44
45
|
});
|
|
46
|
+
|
|
45
47
|
if (!verified) {
|
|
46
48
|
return this.fail();
|
|
47
49
|
}
|
|
@@ -55,10 +57,12 @@ module.exports = class extends BaseRest {
|
|
|
55
57
|
link: user[0].url,
|
|
56
58
|
});
|
|
57
59
|
const { avatarProxy } = think.config();
|
|
60
|
+
|
|
58
61
|
if (avatarProxy) {
|
|
59
62
|
avatarUrl = avatarProxy + '?url=' + encodeURIComponent(avatarUrl);
|
|
60
63
|
}
|
|
61
64
|
user[0].avatar = avatarUrl;
|
|
65
|
+
|
|
62
66
|
return this.success({
|
|
63
67
|
...user[0],
|
|
64
68
|
password: null,
|
|
@@ -12,6 +12,7 @@ module.exports = class extends BaseRest {
|
|
|
12
12
|
SITE_NAME,
|
|
13
13
|
} = process.env;
|
|
14
14
|
const hasMailServie = SMTP_HOST || SMTP_SERVICE;
|
|
15
|
+
|
|
15
16
|
if (!hasMailServie) {
|
|
16
17
|
return this.fail();
|
|
17
18
|
}
|
|
@@ -22,6 +23,7 @@ module.exports = class extends BaseRest {
|
|
|
22
23
|
'Users'
|
|
23
24
|
);
|
|
24
25
|
const user = await userModel.select({ email });
|
|
26
|
+
|
|
25
27
|
if (think.isEmpty(user)) {
|
|
26
28
|
return this.fail();
|
|
27
29
|
}
|
package/src/controller/user.js
CHANGED
|
@@ -23,6 +23,7 @@ module.exports = class extends BaseRest {
|
|
|
23
23
|
offset: Math.max((page - 1) * pageSize, 0),
|
|
24
24
|
}
|
|
25
25
|
);
|
|
26
|
+
|
|
26
27
|
return this.success({
|
|
27
28
|
page,
|
|
28
29
|
totalPages: Math.ceil(count / pageSize),
|
|
@@ -147,8 +148,10 @@ module.exports = class extends BaseRest {
|
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
const socials = ['github', 'twitter', 'facebook', 'google', 'weibo', 'qq'];
|
|
151
|
+
|
|
150
152
|
socials.forEach((social) => {
|
|
151
153
|
const nextSocial = this.post(social);
|
|
154
|
+
|
|
152
155
|
if (think.isString(nextSocial)) {
|
|
153
156
|
updateData[social] = nextSocial;
|
|
154
157
|
}
|
|
@@ -12,18 +12,21 @@ module.exports = class extends BaseRest {
|
|
|
12
12
|
async getAction() {
|
|
13
13
|
const { token, email } = this.get();
|
|
14
14
|
const users = await this.modelInstance.select({ email });
|
|
15
|
+
|
|
15
16
|
if (think.isEmpty(users)) {
|
|
16
17
|
return this.fail(this.locale('USER_NOT_EXIST'));
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
const user = users[0];
|
|
20
21
|
const match = user.type.match(/^verify:(\d{4}):(\d+)$/i);
|
|
22
|
+
|
|
21
23
|
if (!match) {
|
|
22
24
|
return this.fail(this.locale('USER_REGISTED'));
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
if (token === match[1] && Date.now() < parseInt(match[2])) {
|
|
26
28
|
await this.modelInstance.update({ type: 'guest' }, { email });
|
|
29
|
+
|
|
27
30
|
return this.redirect('/ui/login');
|
|
28
31
|
}
|
|
29
32
|
|
package/src/extend/controller.js
CHANGED
|
@@ -4,18 +4,22 @@ const locales = require('../locales');
|
|
|
4
4
|
module.exports = {
|
|
5
5
|
success(...args) {
|
|
6
6
|
this.ctx.success(...args);
|
|
7
|
+
|
|
7
8
|
return think.prevent();
|
|
8
9
|
},
|
|
9
10
|
fail(...args) {
|
|
10
11
|
this.ctx.fail(...args);
|
|
12
|
+
|
|
11
13
|
return think.prevent();
|
|
12
14
|
},
|
|
13
15
|
locale(message, variables) {
|
|
14
16
|
const { lang } = this.get();
|
|
15
17
|
const locale = locales[(lang || '').toLowerCase()];
|
|
18
|
+
|
|
16
19
|
if (locale && locale[message]) {
|
|
17
20
|
message = locale[message];
|
|
18
21
|
}
|
|
22
|
+
|
|
19
23
|
return nunjucks.renderString(message, variables);
|
|
20
24
|
},
|
|
21
25
|
};
|