@waline/vercel 1.36.5 → 1.37.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/development.js +1 -0
- package/index.js +4 -3
- package/package.json +2 -2
- package/src/config/adapter.js +8 -5
- package/src/config/config.js +3 -3
- package/src/controller/article.js +2 -2
- package/src/controller/comment.js +24 -21
- package/src/controller/oauth.js +7 -4
- package/src/controller/rest.js +1 -1
- package/src/controller/user.js +9 -8
- package/src/controller/verification.js +3 -2
- package/src/extend/think.js +33 -19
- package/src/logic/base.js +25 -23
- package/src/logic/comment.js +7 -4
- package/src/logic/db.js +1 -1
- package/src/logic/token.js +2 -2
- package/src/middleware/dashboard.js +1 -0
- package/src/middleware/plugin.js +1 -1
- package/src/service/akismet.js +10 -3
- package/src/service/avatar.js +1 -1
- package/src/service/markdown/highlight.js +1 -1
- package/src/service/markdown/utils.js +5 -5
- package/src/service/markdown/xss.js +5 -10
- package/src/service/notify.js +56 -54
- package/src/service/storage/base.js +1 -1
- package/src/service/storage/cloudbase.js +9 -7
- package/src/service/storage/github.js +24 -18
- package/src/service/storage/leancloud.js +33 -26
- package/src/service/storage/mongodb.js +10 -6
- package/src/service/storage/mysql.js +4 -4
- package/src/service/storage/postgresql.js +5 -5
package/development.js
CHANGED
package/index.js
CHANGED
|
@@ -4,7 +4,7 @@ const path = require('node:path');
|
|
|
4
4
|
const Application = require('thinkjs');
|
|
5
5
|
const Loader = require('thinkjs/lib/loader');
|
|
6
6
|
|
|
7
|
-
module.exports = function (configParams = {}) {
|
|
7
|
+
module.exports = function main(configParams = {}) {
|
|
8
8
|
const { env, ...config } = configParams;
|
|
9
9
|
|
|
10
10
|
const app = new Application({
|
|
@@ -20,10 +20,11 @@ module.exports = function (configParams = {}) {
|
|
|
20
20
|
|
|
21
21
|
loader.loadAll('worker');
|
|
22
22
|
|
|
23
|
+
// oxlint-disable-next-line func-names
|
|
23
24
|
return function (req, res) {
|
|
24
|
-
for (const
|
|
25
|
+
for (const key in config) {
|
|
25
26
|
// fix https://github.com/walinejs/waline/issues/2649 with alias model config name
|
|
26
|
-
think.config(
|
|
27
|
+
think.config(key === 'model' ? 'customModel' : key, config[key]);
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
return think
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/vercel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.37.0",
|
|
4
4
|
"description": "vercel server for waline comment system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"blog",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"@mdit/plugin-sup": "0.22.5-cjs.0",
|
|
30
30
|
"akismet": "^2.0.7",
|
|
31
31
|
"dompurify": "^3.3.1",
|
|
32
|
-
"dy-node-ip2region": "^1.0.1",
|
|
33
32
|
"fast-csv": "^5.0.5",
|
|
34
33
|
"form-data": "^4.0.5",
|
|
34
|
+
"ip2region": "^2.3.0",
|
|
35
35
|
"jsdom": "^19.0.0",
|
|
36
36
|
"jsonwebtoken": "^9.0.3",
|
|
37
37
|
"koa-compose": "^4.1.0",
|
package/src/config/adapter.js
CHANGED
|
@@ -6,9 +6,10 @@ const Postgresql = require('think-model-postgresql');
|
|
|
6
6
|
let Sqlite;
|
|
7
7
|
|
|
8
8
|
try {
|
|
9
|
+
// oxlint-disable-next-line node/global-require
|
|
9
10
|
Sqlite = require('think-model-sqlite');
|
|
10
11
|
} catch (err) {
|
|
11
|
-
//
|
|
12
|
+
// oxlint-disable-next-line typescript/no-extraneous-class
|
|
12
13
|
Sqlite = class {};
|
|
13
14
|
console.log(err);
|
|
14
15
|
}
|
|
@@ -65,11 +66,11 @@ if (MONGO_AUTHSOURCE) mongoOpt.authSource = MONGO_AUTHSOURCE;
|
|
|
65
66
|
if (MONGO_DB) {
|
|
66
67
|
type = 'mongo';
|
|
67
68
|
for (const envKeys in process.env) {
|
|
68
|
-
if (
|
|
69
|
+
if (envKeys.includes('MONGO_OPT_')) {
|
|
69
70
|
const key = envKeys
|
|
70
71
|
.slice(10)
|
|
71
72
|
.toLocaleLowerCase()
|
|
72
|
-
.
|
|
73
|
+
.replaceAll(/_([a-z])/g, (_, b) => b.toUpperCase());
|
|
73
74
|
|
|
74
75
|
mongoOpt[key] = process.env[envKeys];
|
|
75
76
|
}
|
|
@@ -88,7 +89,9 @@ exports.model = {
|
|
|
88
89
|
type,
|
|
89
90
|
common: {
|
|
90
91
|
logSql: true,
|
|
91
|
-
logger: (msg) =>
|
|
92
|
+
logger: (msg) => {
|
|
93
|
+
think.logger.info(msg);
|
|
94
|
+
},
|
|
92
95
|
},
|
|
93
96
|
|
|
94
97
|
mongo: {
|
|
@@ -114,7 +117,7 @@ exports.model = {
|
|
|
114
117
|
connectionLimit: 1,
|
|
115
118
|
prefix: PG_PREFIX || POSTGRES_PREFIX || 'wl_',
|
|
116
119
|
ssl:
|
|
117
|
-
(PG_SSL || POSTGRES_SSL)
|
|
120
|
+
(PG_SSL || POSTGRES_SSL) === 'true' || POSTGRES_URL?.includes('sslmode=require')
|
|
118
121
|
? {
|
|
119
122
|
rejectUnauthorized: false,
|
|
120
123
|
}
|
package/src/config/config.js
CHANGED
|
@@ -100,7 +100,7 @@ if (isFalse(MARKDOWN_HIGHLIGHT)) markdown.config.highlight = false;
|
|
|
100
100
|
let avatarProxy = '';
|
|
101
101
|
|
|
102
102
|
if (AVATAR_PROXY) {
|
|
103
|
-
avatarProxy =
|
|
103
|
+
avatarProxy = isFalse(AVATAR_PROXY) ? '' : AVATAR_PROXY;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const oauthUrl = OAUTH_URL || 'https://oauth.lithub.cc';
|
|
@@ -111,10 +111,10 @@ module.exports = {
|
|
|
111
111
|
jwtKey,
|
|
112
112
|
forbiddenWords,
|
|
113
113
|
disallowIPList: [],
|
|
114
|
-
secureDomains: SECURE_DOMAINS ? SECURE_DOMAINS.split(/\s*,\s*/) :
|
|
114
|
+
secureDomains: SECURE_DOMAINS ? SECURE_DOMAINS.split(/\s*,\s*/) : null,
|
|
115
115
|
disableUserAgent: DISABLE_USERAGENT && !isFalse(DISABLE_USERAGENT),
|
|
116
116
|
disableRegion: DISABLE_REGION && !isFalse(DISABLE_REGION),
|
|
117
|
-
levels: !LEVELS || isFalse(LEVELS) ? false : LEVELS.split(/\s*,\s*/).map(
|
|
117
|
+
levels: !LEVELS || isFalse(LEVELS) ? false : LEVELS.split(/\s*,\s*/).map(Number),
|
|
118
118
|
|
|
119
119
|
audit: COMMENT_AUDIT && !isFalse(COMMENT_AUDIT),
|
|
120
120
|
avatarProxy,
|
|
@@ -11,14 +11,14 @@ module.exports = class extends BaseRest {
|
|
|
11
11
|
const { deprecated } = this.ctx.state;
|
|
12
12
|
|
|
13
13
|
// path is required
|
|
14
|
-
if (!Array.isArray(path) ||
|
|
14
|
+
if (!Array.isArray(path) || path.length === 0) {
|
|
15
15
|
return this.jsonOrSuccess(0);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const resp = await this.modelInstance.select({ url: ['IN', path] });
|
|
19
19
|
|
|
20
20
|
if (think.isEmpty(resp)) {
|
|
21
|
-
const counters =
|
|
21
|
+
const counters = Array(path.length).fill(
|
|
22
22
|
type.length === 1 && deprecated
|
|
23
23
|
? 0
|
|
24
24
|
: type.reduce((o, field) => {
|
|
@@ -4,12 +4,13 @@ const { getMarkdownParser } = require('../service/markdown/index.js');
|
|
|
4
4
|
|
|
5
5
|
const markdownParser = getMarkdownParser();
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// oxlint-disable-next-line max-statements
|
|
8
|
+
const formatCmt = async (
|
|
8
9
|
{ ua, ip, ...comment },
|
|
9
10
|
users = [],
|
|
10
11
|
{ avatarProxy, deprecated },
|
|
11
12
|
loginUser,
|
|
12
|
-
) {
|
|
13
|
+
) => {
|
|
13
14
|
ua = think.uaParser(ua);
|
|
14
15
|
if (!think.config('disableUserAgent')) {
|
|
15
16
|
comment.browser = `${ua.browser.name || ''}${(ua.browser.version || '')
|
|
@@ -33,7 +34,7 @@ async function formatCmt(
|
|
|
33
34
|
|
|
34
35
|
comment.avatar =
|
|
35
36
|
avatarProxy && !avatarUrl.includes(avatarProxy)
|
|
36
|
-
? avatarProxy
|
|
37
|
+
? `${avatarProxy}?url=${encodeURIComponent(avatarUrl)}`
|
|
37
38
|
: avatarUrl;
|
|
38
39
|
|
|
39
40
|
const isAdmin = loginUser && loginUser.type === 'administrator';
|
|
@@ -67,7 +68,7 @@ async function formatCmt(
|
|
|
67
68
|
delete comment.updatedAt;
|
|
68
69
|
|
|
69
70
|
return comment;
|
|
70
|
-
}
|
|
71
|
+
};
|
|
71
72
|
|
|
72
73
|
module.exports = class extends BaseRest {
|
|
73
74
|
constructor(ctx) {
|
|
@@ -79,12 +80,12 @@ module.exports = class extends BaseRest {
|
|
|
79
80
|
const { type } = this.get();
|
|
80
81
|
|
|
81
82
|
const fnMap = {
|
|
82
|
-
recent: this
|
|
83
|
-
count: this
|
|
84
|
-
list: this
|
|
83
|
+
recent: this['getRecentCommentList'],
|
|
84
|
+
count: this['getCommentCount'],
|
|
85
|
+
list: this['getAdminCommentList'],
|
|
85
86
|
};
|
|
86
87
|
|
|
87
|
-
const fn = fnMap[type] || this
|
|
88
|
+
const fn = fnMap[type] || this['getCommentList'];
|
|
88
89
|
const data = await fn.call(this);
|
|
89
90
|
|
|
90
91
|
return this.jsonOrSuccess(data);
|
|
@@ -122,7 +123,7 @@ module.exports = class extends BaseRest {
|
|
|
122
123
|
|
|
123
124
|
if (
|
|
124
125
|
think.isArray(disallowIPList) &&
|
|
125
|
-
disallowIPList.length &&
|
|
126
|
+
disallowIPList.length > 0 &&
|
|
126
127
|
disallowIPList.includes(data.ip)
|
|
127
128
|
) {
|
|
128
129
|
think.logger.debug(`Comment IP ${data.ip} is in disallowIPList`);
|
|
@@ -171,7 +172,9 @@ module.exports = class extends BaseRest {
|
|
|
171
172
|
think.logger.debug(`Comment initial status is ${data.status}`);
|
|
172
173
|
|
|
173
174
|
if (data.status === 'approved') {
|
|
174
|
-
const spam = await akismet(data, this.ctx.serverURL).catch((err) =>
|
|
175
|
+
const spam = await akismet(data, this.ctx.serverURL).catch((err) => {
|
|
176
|
+
console.log(err);
|
|
177
|
+
}); // ignore akismet error
|
|
175
178
|
|
|
176
179
|
if (spam === true) {
|
|
177
180
|
data.status = 'spam';
|
|
@@ -272,7 +275,7 @@ module.exports = class extends BaseRest {
|
|
|
272
275
|
async putAction() {
|
|
273
276
|
const { userInfo } = this.ctx.state;
|
|
274
277
|
const isAdmin = userInfo.type === 'administrator';
|
|
275
|
-
|
|
278
|
+
const data = isAdmin ? this.post() : this.post('comment,like');
|
|
276
279
|
let oldData = await this.modelInstance.select({ objectId: this.id });
|
|
277
280
|
|
|
278
281
|
if (think.isEmpty(oldData) || think.isEmpty(data)) {
|
|
@@ -473,10 +476,10 @@ module.exports = class extends BaseRest {
|
|
|
473
476
|
}
|
|
474
477
|
|
|
475
478
|
const userModel = this.getModel('Users');
|
|
476
|
-
const user_ids =
|
|
479
|
+
const user_ids = [...new Set(comments.map(({ user_id }) => user_id).filter((v) => v))];
|
|
477
480
|
let users = [];
|
|
478
481
|
|
|
479
|
-
if (user_ids.length) {
|
|
482
|
+
if (user_ids.length > 0) {
|
|
480
483
|
users = await userModel.select(
|
|
481
484
|
{ objectId: ['IN', user_ids] },
|
|
482
485
|
{
|
|
@@ -491,12 +494,12 @@ module.exports = class extends BaseRest {
|
|
|
491
494
|
_complex: {},
|
|
492
495
|
};
|
|
493
496
|
|
|
494
|
-
if (user_ids.length) {
|
|
497
|
+
if (user_ids.length > 0) {
|
|
495
498
|
countWhere._complex.user_id = ['IN', user_ids];
|
|
496
499
|
}
|
|
497
|
-
const mails =
|
|
500
|
+
const mails = [...new Set(comments.map(({ mail }) => mail).filter((v) => v))];
|
|
498
501
|
|
|
499
|
-
if (mails.length) {
|
|
502
|
+
if (mails.length > 0) {
|
|
500
503
|
countWhere._complex.mail = ['IN', mails];
|
|
501
504
|
}
|
|
502
505
|
if (!think.isEmpty(countWhere._complex)) {
|
|
@@ -610,11 +613,11 @@ module.exports = class extends BaseRest {
|
|
|
610
613
|
});
|
|
611
614
|
|
|
612
615
|
const userModel = this.getModel('Users');
|
|
613
|
-
const user_ids =
|
|
616
|
+
const user_ids = [...new Set(comments.map(({ user_id }) => user_id).filter((v) => v))];
|
|
614
617
|
|
|
615
618
|
let users = [];
|
|
616
619
|
|
|
617
|
-
if (user_ids.length) {
|
|
620
|
+
if (user_ids.length > 0) {
|
|
618
621
|
users = await userModel.select(
|
|
619
622
|
{ objectId: ['IN', user_ids] },
|
|
620
623
|
{
|
|
@@ -679,11 +682,11 @@ module.exports = class extends BaseRest {
|
|
|
679
682
|
});
|
|
680
683
|
|
|
681
684
|
const userModel = this.getModel('Users');
|
|
682
|
-
const user_ids =
|
|
685
|
+
const user_ids = [...new Set(comments.map(({ user_id }) => user_id).filter((v) => v))];
|
|
683
686
|
|
|
684
687
|
let users = [];
|
|
685
688
|
|
|
686
|
-
if (user_ids.length) {
|
|
689
|
+
if (user_ids.length > 0) {
|
|
687
690
|
users = await userModel.select(
|
|
688
691
|
{ objectId: ['IN', user_ids] },
|
|
689
692
|
{
|
|
@@ -707,7 +710,7 @@ module.exports = class extends BaseRest {
|
|
|
707
710
|
async getCommentCount() {
|
|
708
711
|
const { url } = this.get();
|
|
709
712
|
const { userInfo } = this.ctx.state;
|
|
710
|
-
const where = Array.isArray(url) && url.length ? { url: ['IN', url] } : {};
|
|
713
|
+
const where = Array.isArray(url) && url.length > 0 ? { url: ['IN', url] } : {};
|
|
711
714
|
|
|
712
715
|
if (think.isEmpty(userInfo)) {
|
|
713
716
|
where.status = ['NOT IN', ['waiting', 'spam']];
|
package/src/controller/oauth.js
CHANGED
|
@@ -19,12 +19,13 @@ module.exports = class extends think.Controller {
|
|
|
19
19
|
type,
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
this.redirect(
|
|
23
23
|
think.buildUrl(`${oauthUrl}/${type}`, {
|
|
24
24
|
redirect: redirectUrl,
|
|
25
25
|
state: this.ctx.state.token || '',
|
|
26
26
|
}),
|
|
27
27
|
);
|
|
28
|
+
return;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -64,7 +65,8 @@ module.exports = class extends think.Controller {
|
|
|
64
65
|
const token = jwt.sign(userBySocial[0].objectId, this.config('jwtKey'));
|
|
65
66
|
|
|
66
67
|
if (redirect) {
|
|
67
|
-
|
|
68
|
+
this.redirect(think.buildUrl(redirect, { token }));
|
|
69
|
+
return;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
return this.success();
|
|
@@ -84,7 +86,8 @@ module.exports = class extends think.Controller {
|
|
|
84
86
|
objectId: current.objectId,
|
|
85
87
|
});
|
|
86
88
|
|
|
87
|
-
|
|
89
|
+
this.redirect('/ui/profile');
|
|
90
|
+
return;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
// when user has not login, then we create account by the social type!
|
|
@@ -108,6 +111,6 @@ module.exports = class extends think.Controller {
|
|
|
108
111
|
// and then generate token!
|
|
109
112
|
const token = jwt.sign(user.objectId, this.config('jwtKey'));
|
|
110
113
|
|
|
111
|
-
|
|
114
|
+
this.redirect(redirect + (redirect.includes('?') ? '&' : '?') + 'token=' + token);
|
|
112
115
|
}
|
|
113
116
|
};
|
package/src/controller/rest.js
CHANGED
|
@@ -21,7 +21,7 @@ module.exports = class extends think.Controller {
|
|
|
21
21
|
const filename = this.__filename || __filename;
|
|
22
22
|
const last = filename.lastIndexOf(path.sep);
|
|
23
23
|
|
|
24
|
-
return filename.
|
|
24
|
+
return filename.slice(last + 1, filename.length - last - 4);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
getId() {
|
package/src/controller/user.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const BaseRest = require('./rest.js');
|
|
2
2
|
|
|
3
|
-
module.exports = class extends BaseRest {
|
|
3
|
+
module.exports = class UserController extends BaseRest {
|
|
4
4
|
constructor(...args) {
|
|
5
5
|
super(...args);
|
|
6
6
|
this.modelInstance = this.getModel('Users');
|
|
@@ -80,7 +80,7 @@ module.exports = class extends BaseRest {
|
|
|
80
80
|
|
|
81
81
|
try {
|
|
82
82
|
const notify = this.service('notify', this);
|
|
83
|
-
const apiUrl = think.buildUrl(this.ctx.serverURL
|
|
83
|
+
const apiUrl = think.buildUrl(`${this.ctx.serverURL}/verification`, {
|
|
84
84
|
token,
|
|
85
85
|
email: data.email,
|
|
86
86
|
});
|
|
@@ -179,6 +179,7 @@ module.exports = class extends BaseRest {
|
|
|
179
179
|
return this.success();
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
// oxlint-disable-next-line max-statements
|
|
182
183
|
async getUsersListByCount() {
|
|
183
184
|
const { pageSize } = this.get();
|
|
184
185
|
const commentModel = this.getModel('Comment');
|
|
@@ -196,9 +197,9 @@ module.exports = class extends BaseRest {
|
|
|
196
197
|
|
|
197
198
|
const userIds = counts.filter(({ user_id }) => user_id).map(({ user_id }) => user_id);
|
|
198
199
|
|
|
199
|
-
|
|
200
|
+
const usersMap = {};
|
|
200
201
|
|
|
201
|
-
if (userIds.length) {
|
|
202
|
+
if (userIds.length > 0) {
|
|
202
203
|
const users = await this.modelInstance.select({
|
|
203
204
|
objectId: ['IN', userIds],
|
|
204
205
|
});
|
|
@@ -220,7 +221,7 @@ module.exports = class extends BaseRest {
|
|
|
220
221
|
let level = 0;
|
|
221
222
|
|
|
222
223
|
if (user.count) {
|
|
223
|
-
const _level = think.findLastIndex(this.config('levels'), (
|
|
224
|
+
const _level = think.findLastIndex(this.config('levels'), (level) => level <= user.count);
|
|
224
225
|
|
|
225
226
|
if (_level !== -1) {
|
|
226
227
|
level = _level;
|
|
@@ -233,7 +234,7 @@ module.exports = class extends BaseRest {
|
|
|
233
234
|
const { display_name: nick, url: link, avatar: avatarUrl, label } = users[count.user_id];
|
|
234
235
|
const avatar =
|
|
235
236
|
avatarProxy && !avatarUrl.includes(avatarProxy)
|
|
236
|
-
? avatarProxy
|
|
237
|
+
? `${avatarProxy}?url=${encodeURIComponent(avatarUrl)}`
|
|
237
238
|
: avatarUrl;
|
|
238
239
|
|
|
239
240
|
Object.assign(user, { nick, link, avatar, label });
|
|
@@ -246,7 +247,7 @@ module.exports = class extends BaseRest {
|
|
|
246
247
|
if (think.isEmpty(comments)) {
|
|
247
248
|
continue;
|
|
248
249
|
}
|
|
249
|
-
const comment = comments
|
|
250
|
+
const [comment] = comments;
|
|
250
251
|
|
|
251
252
|
if (think.isEmpty(comment)) {
|
|
252
253
|
continue;
|
|
@@ -255,7 +256,7 @@ module.exports = class extends BaseRest {
|
|
|
255
256
|
const avatarUrl = await think.service('avatar').stringify(comment);
|
|
256
257
|
const avatar =
|
|
257
258
|
avatarProxy && !avatarUrl.includes(avatarProxy)
|
|
258
|
-
? avatarProxy
|
|
259
|
+
? `${avatarProxy}?url=${encodeURIComponent(avatarUrl)}`
|
|
259
260
|
: avatarUrl;
|
|
260
261
|
|
|
261
262
|
Object.assign(user, { nick, link, avatar });
|
|
@@ -21,10 +21,11 @@ module.exports = class extends BaseRest {
|
|
|
21
21
|
return this.fail(this.locale('USER_REGISTERED'));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
if (token === match[1] && Date.now() < parseInt(match[2])) {
|
|
24
|
+
if (token === match[1] && Date.now() < Number.parseInt(match[2])) {
|
|
25
25
|
await this.modelInstance.update({ type: 'guest' }, { email });
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
this.redirect('/ui/login');
|
|
28
|
+
return;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
return this.fail(this.locale('TOKEN_EXPIRED'));
|
package/src/extend/think.js
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
const
|
|
2
|
-
const helper = require('think-helper');
|
|
1
|
+
const IP2Region = require('ip2region').default;
|
|
3
2
|
const parser = require('ua-parser-js');
|
|
4
3
|
|
|
5
4
|
const preventMessage = 'PREVENT_NEXT_PROCESS';
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
// Cached IP2Region instance using IIFE closure pattern
|
|
7
|
+
// Instance is created on first access and reused for all subsequent calls
|
|
8
|
+
const getIP2RegionInstance = (() => {
|
|
9
|
+
let instance = null;
|
|
10
|
+
|
|
11
|
+
return () => {
|
|
12
|
+
if (!instance) {
|
|
13
|
+
instance = new IP2Region({
|
|
14
|
+
ipv4db: process.env.IP2REGION_DB_V4 || process.env.IP2REGION_DB,
|
|
15
|
+
ipv6db: process.env.IP2REGION_DB_V6,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return instance;
|
|
20
|
+
};
|
|
21
|
+
})();
|
|
8
22
|
|
|
9
23
|
const OS_VERSION_MAP = {
|
|
10
24
|
Windows: {
|
|
@@ -34,15 +48,17 @@ module.exports = {
|
|
|
34
48
|
},
|
|
35
49
|
promiseAllQueue(promises, taskNum) {
|
|
36
50
|
return new Promise((resolve, reject) => {
|
|
37
|
-
if (
|
|
38
|
-
|
|
51
|
+
if (promises.length === 0) {
|
|
52
|
+
resolve();
|
|
53
|
+
|
|
54
|
+
return;
|
|
39
55
|
}
|
|
40
56
|
|
|
41
57
|
const ret = [];
|
|
42
58
|
let index = 0;
|
|
43
59
|
let count = 0;
|
|
44
60
|
|
|
45
|
-
|
|
61
|
+
const runTask = () => {
|
|
46
62
|
const idx = index;
|
|
47
63
|
|
|
48
64
|
index += 1;
|
|
@@ -59,7 +75,7 @@ module.exports = {
|
|
|
59
75
|
|
|
60
76
|
return runTask();
|
|
61
77
|
}, reject);
|
|
62
|
-
}
|
|
78
|
+
};
|
|
63
79
|
|
|
64
80
|
for (let i = 0; i < taskNum; i++) {
|
|
65
81
|
runTask();
|
|
@@ -67,19 +83,17 @@ module.exports = {
|
|
|
67
83
|
});
|
|
68
84
|
},
|
|
69
85
|
async ip2region(ip, { depth = 1 }) {
|
|
70
|
-
if (!ip
|
|
86
|
+
if (!ip) return '';
|
|
71
87
|
|
|
72
88
|
try {
|
|
73
|
-
const
|
|
74
|
-
const result = await search(ip);
|
|
89
|
+
const res = getIP2RegionInstance().search(ip);
|
|
75
90
|
|
|
76
|
-
if (!
|
|
91
|
+
if (!res) {
|
|
77
92
|
return '';
|
|
78
93
|
}
|
|
79
|
-
const { region } = result;
|
|
80
|
-
const [, , province, city, isp] = region.split('|');
|
|
81
|
-
const address = Array.from(new Set([province, city, isp].filter((v) => v)));
|
|
82
94
|
|
|
95
|
+
const { province, city, isp } = res;
|
|
96
|
+
const address = [...new Set([province, city, isp].filter(Boolean))];
|
|
83
97
|
return address.slice(0, depth).join(' ');
|
|
84
98
|
} catch (err) {
|
|
85
99
|
console.log(err);
|
|
@@ -104,7 +118,7 @@ module.exports = {
|
|
|
104
118
|
return defaultLevel;
|
|
105
119
|
}
|
|
106
120
|
|
|
107
|
-
const level = think.findLastIndex(levels, (
|
|
121
|
+
const level = think.findLastIndex(levels, (level) => level <= val);
|
|
108
122
|
|
|
109
123
|
return level === -1 ? defaultLevel : level;
|
|
110
124
|
},
|
|
@@ -139,7 +153,7 @@ module.exports = {
|
|
|
139
153
|
}
|
|
140
154
|
|
|
141
155
|
if (think.isArray(middleware)) {
|
|
142
|
-
return middleware.filter((
|
|
156
|
+
return middleware.filter((middleware) => think.isFunction(middleware));
|
|
143
157
|
}
|
|
144
158
|
});
|
|
145
159
|
|
|
@@ -147,8 +161,8 @@ module.exports = {
|
|
|
147
161
|
},
|
|
148
162
|
getPluginHook(hookName) {
|
|
149
163
|
return think
|
|
150
|
-
.pluginMap('hooks', (hook) => (think.isFunction(hook[hookName]) ? hook[hookName] :
|
|
151
|
-
.filter(
|
|
164
|
+
.pluginMap('hooks', (hook) => (think.isFunction(hook[hookName]) ? hook[hookName] : null))
|
|
165
|
+
.filter(Boolean);
|
|
152
166
|
},
|
|
153
167
|
buildUrl(path, query = {}) {
|
|
154
168
|
const notEmptyQuery = {};
|
|
@@ -165,7 +179,7 @@ module.exports = {
|
|
|
165
179
|
let destUrl = path;
|
|
166
180
|
|
|
167
181
|
if (destUrl && notEmptyQueryStr) {
|
|
168
|
-
destUrl += destUrl.
|
|
182
|
+
destUrl += destUrl.includes('?') ? '&' : '?';
|
|
169
183
|
}
|
|
170
184
|
if (notEmptyQueryStr) {
|
|
171
185
|
destUrl += notEmptyQueryStr;
|
package/src/logic/base.js
CHANGED
|
@@ -3,7 +3,7 @@ const qs = require('node:querystring');
|
|
|
3
3
|
|
|
4
4
|
const jwt = require('jsonwebtoken');
|
|
5
5
|
|
|
6
|
-
module.exports = class extends think.Logic {
|
|
6
|
+
module.exports = class BaseLogic extends think.Logic {
|
|
7
7
|
constructor(...args) {
|
|
8
8
|
super(...args);
|
|
9
9
|
this.modelInstance = this.getModel('Users');
|
|
@@ -11,17 +11,18 @@ module.exports = class extends think.Logic {
|
|
|
11
11
|
this.id = this.getId();
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
// oxlint-disable-next-line max-statements
|
|
14
15
|
async __before() {
|
|
15
16
|
const referrer = this.ctx.referrer(true);
|
|
16
|
-
let origin = this.ctx
|
|
17
|
+
let { origin } = this.ctx;
|
|
17
18
|
|
|
18
19
|
if (origin) {
|
|
19
20
|
try {
|
|
20
21
|
const parsedOrigin = new URL(origin);
|
|
21
22
|
|
|
22
23
|
origin = parsedOrigin.hostname;
|
|
23
|
-
} catch (
|
|
24
|
-
console.error('Invalid origin format:', origin,
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.error('Invalid origin format:', origin, err);
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -39,9 +40,10 @@ module.exports = class extends think.Logic {
|
|
|
39
40
|
// 'api.weibo.com',
|
|
40
41
|
// 'graph.qq.com',
|
|
41
42
|
);
|
|
42
|
-
secureDomains =
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
secureDomains = [
|
|
44
|
+
...secureDomains,
|
|
45
|
+
...this.ctx.state.oauthServices.map(({ origin }) => origin),
|
|
46
|
+
];
|
|
45
47
|
|
|
46
48
|
// 转换可能的正则表达式字符串为正则表达式对象
|
|
47
49
|
secureDomains = secureDomains
|
|
@@ -50,8 +52,8 @@ module.exports = class extends think.Logic {
|
|
|
50
52
|
if (typeof domain === 'string' && domain.startsWith('/') && domain.endsWith('/')) {
|
|
51
53
|
try {
|
|
52
54
|
return new RegExp(domain.slice(1, -1)); // 去掉斜杠并创建 RegExp 对象
|
|
53
|
-
} catch (
|
|
54
|
-
console.error('Invalid regex pattern in secureDomains:', domain,
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.error('Invalid regex pattern in secureDomains:', domain, err);
|
|
55
57
|
|
|
56
58
|
return null;
|
|
57
59
|
}
|
|
@@ -62,7 +64,7 @@ module.exports = class extends think.Logic {
|
|
|
62
64
|
.filter(Boolean); // 过滤掉无效的正则表达式
|
|
63
65
|
|
|
64
66
|
// 有 referrer 检查 referrer,没有则检查 origin
|
|
65
|
-
const checking = referrer
|
|
67
|
+
const checking = referrer || origin;
|
|
66
68
|
const isSafe = secureDomains.some((domain) =>
|
|
67
69
|
think.isFunction(domain.test) ? domain.test(checking) : domain === checking,
|
|
68
70
|
);
|
|
@@ -84,8 +86,8 @@ module.exports = class extends think.Logic {
|
|
|
84
86
|
|
|
85
87
|
try {
|
|
86
88
|
userId = jwt.verify(token, think.config('jwtKey'));
|
|
87
|
-
} catch (
|
|
88
|
-
think.logger.debug(
|
|
89
|
+
} catch (err) {
|
|
90
|
+
think.logger.debug(err);
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
if (think.isEmpty(userId) || !think.isString(userId)) {
|
|
@@ -113,19 +115,19 @@ module.exports = class extends think.Logic {
|
|
|
113
115
|
return;
|
|
114
116
|
}
|
|
115
117
|
|
|
116
|
-
const userInfo = user
|
|
118
|
+
const [userInfo] = user;
|
|
117
119
|
|
|
118
|
-
let avatarUrl =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
let avatarUrl =
|
|
121
|
+
userInfo.avatar ||
|
|
122
|
+
(await think.service('avatar').stringify({
|
|
123
|
+
mail: userInfo.email,
|
|
124
|
+
nick: userInfo.display_name,
|
|
125
|
+
link: userInfo.url,
|
|
126
|
+
}));
|
|
125
127
|
const { avatarProxy } = think.config();
|
|
126
128
|
|
|
127
129
|
if (avatarProxy) {
|
|
128
|
-
avatarUrl = avatarProxy
|
|
130
|
+
avatarUrl = `${avatarProxy}?url=${encodeURIComponent(avatarUrl)}`;
|
|
129
131
|
}
|
|
130
132
|
userInfo.avatar = avatarUrl;
|
|
131
133
|
this.ctx.state.userInfo = userInfo;
|
|
@@ -136,7 +138,7 @@ module.exports = class extends think.Logic {
|
|
|
136
138
|
const filename = this.__filename || __filename;
|
|
137
139
|
const last = filename.lastIndexOf(path.sep);
|
|
138
140
|
|
|
139
|
-
return filename.
|
|
141
|
+
return filename.slice(last + 1, filename.length - last - 4);
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
getId() {
|
|
@@ -193,7 +195,7 @@ module.exports = class extends think.Logic {
|
|
|
193
195
|
remoteip: this.ctx.ip,
|
|
194
196
|
});
|
|
195
197
|
|
|
196
|
-
const requestUrl = method === 'GET' ? api
|
|
198
|
+
const requestUrl = method === 'GET' ? `${api}?${query}` : api;
|
|
197
199
|
const options =
|
|
198
200
|
method === 'GET'
|
|
199
201
|
? {}
|