@waline/vercel 1.16.0 → 1.17.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/package.json +5 -5
- package/src/config/config.js +1 -1
- package/src/controller/comment.js +25 -3
- package/src/controller/token.js +11 -6
- package/src/controller/user.js +32 -2
- package/src/extend/think.js +12 -4
- package/src/logic/base.js +17 -0
- package/src/logic/user.js +24 -0
- package/src/service/storage/leancloud.js +127 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/vercel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.2",
|
|
4
4
|
"description": "vercel server for waline comment system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"waline",
|
|
@@ -26,15 +26,15 @@
|
|
|
26
26
|
"jsonwebtoken": "^8.5.1",
|
|
27
27
|
"katex": "^0.15.3",
|
|
28
28
|
"leancloud-storage": "^4.12.2",
|
|
29
|
-
"markdown-it": "^
|
|
30
|
-
"markdown-it-emoji": "^2.0.
|
|
29
|
+
"markdown-it": "^13.0.1",
|
|
30
|
+
"markdown-it-emoji": "^2.0.2",
|
|
31
31
|
"markdown-it-sub": "^1.0.0",
|
|
32
32
|
"markdown-it-sup": "^1.0.0",
|
|
33
33
|
"mathjax-full": "^3.2.0",
|
|
34
|
-
"nodemailer": "^6.7.
|
|
34
|
+
"nodemailer": "^6.7.4",
|
|
35
35
|
"nunjucks": "^3.2.3",
|
|
36
36
|
"phpass": "^0.1.1",
|
|
37
|
-
"prismjs": "^1.
|
|
37
|
+
"prismjs": "^1.28.0",
|
|
38
38
|
"request": "^2.88.2",
|
|
39
39
|
"request-promise-native": "^1.0.9",
|
|
40
40
|
"speakeasy": "^2.0.0",
|
package/src/config/config.js
CHANGED
|
@@ -107,7 +107,7 @@ module.exports = {
|
|
|
107
107
|
disallowIPList: [],
|
|
108
108
|
secureDomains: SECURE_DOMAINS ? SECURE_DOMAINS.split(/\s*,\s*/) : undefined,
|
|
109
109
|
disableUserAgent: DISABLE_USERAGENT && !isFalse(DISABLE_USERAGENT),
|
|
110
|
-
|
|
110
|
+
disableRegion: DISABLE_REGION && !isFalse(DISABLE_REGION),
|
|
111
111
|
levels:
|
|
112
112
|
!LEVELS || isFalse(LEVELS)
|
|
113
113
|
? false
|
|
@@ -25,6 +25,7 @@ async function formatCmt(
|
|
|
25
25
|
comment.mail = user.email;
|
|
26
26
|
comment.link = user.url;
|
|
27
27
|
comment.type = user.type;
|
|
28
|
+
comment.label = user.label;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
const avatarUrl =
|
|
@@ -112,7 +113,14 @@ module.exports = class extends BaseRest {
|
|
|
112
113
|
users = await userModel.select(
|
|
113
114
|
{ objectId: ['IN', user_ids] },
|
|
114
115
|
{
|
|
115
|
-
field: [
|
|
116
|
+
field: [
|
|
117
|
+
'display_name',
|
|
118
|
+
'email',
|
|
119
|
+
'url',
|
|
120
|
+
'type',
|
|
121
|
+
'avatar',
|
|
122
|
+
'label',
|
|
123
|
+
],
|
|
116
124
|
}
|
|
117
125
|
);
|
|
118
126
|
}
|
|
@@ -192,7 +200,14 @@ module.exports = class extends BaseRest {
|
|
|
192
200
|
users = await userModel.select(
|
|
193
201
|
{ objectId: ['IN', user_ids] },
|
|
194
202
|
{
|
|
195
|
-
field: [
|
|
203
|
+
field: [
|
|
204
|
+
'display_name',
|
|
205
|
+
'email',
|
|
206
|
+
'url',
|
|
207
|
+
'type',
|
|
208
|
+
'avatar',
|
|
209
|
+
'label',
|
|
210
|
+
],
|
|
196
211
|
}
|
|
197
212
|
);
|
|
198
213
|
}
|
|
@@ -314,7 +329,14 @@ module.exports = class extends BaseRest {
|
|
|
314
329
|
users = await userModel.select(
|
|
315
330
|
{ objectId: ['IN', user_ids] },
|
|
316
331
|
{
|
|
317
|
-
field: [
|
|
332
|
+
field: [
|
|
333
|
+
'display_name',
|
|
334
|
+
'email',
|
|
335
|
+
'url',
|
|
336
|
+
'type',
|
|
337
|
+
'avatar',
|
|
338
|
+
'label',
|
|
339
|
+
],
|
|
318
340
|
}
|
|
319
341
|
);
|
|
320
342
|
}
|
package/src/controller/token.js
CHANGED
|
@@ -47,13 +47,18 @@ module.exports = class extends BaseRest {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
avatar
|
|
53
|
-
|
|
50
|
+
let avatarUrl = user[0].avatar
|
|
51
|
+
? user[0].avatar
|
|
52
|
+
: await think.service('avatar').stringify({
|
|
53
|
+
mail: user[0].email,
|
|
54
|
+
nick: user[0].display_name,
|
|
55
|
+
link: user[0].url,
|
|
56
|
+
});
|
|
57
|
+
const { avatarProxy } = think.config();
|
|
58
|
+
if (avatarProxy) {
|
|
59
|
+
avatarUrl = avatarProxy + '?url=' + encodeURIComponent(avatarUrl);
|
|
54
60
|
}
|
|
55
|
-
user[0].avatar =
|
|
56
|
-
|
|
61
|
+
user[0].avatar = avatarUrl;
|
|
57
62
|
return this.success({
|
|
58
63
|
...user[0],
|
|
59
64
|
password: null,
|
package/src/controller/user.js
CHANGED
|
@@ -11,6 +11,26 @@ module.exports = class extends BaseRest {
|
|
|
11
11
|
);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
async getAction() {
|
|
15
|
+
const { page, pageSize } = this.get();
|
|
16
|
+
|
|
17
|
+
const count = await this.modelInstance.count({});
|
|
18
|
+
const users = await this.modelInstance.select(
|
|
19
|
+
{},
|
|
20
|
+
{
|
|
21
|
+
desc: 'createdAt',
|
|
22
|
+
limit: pageSize,
|
|
23
|
+
offset: Math.max((page - 1) * pageSize, 0),
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
return this.success({
|
|
27
|
+
page,
|
|
28
|
+
totalPages: Math.ceil(count / pageSize),
|
|
29
|
+
pageSize,
|
|
30
|
+
data: users,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
14
34
|
async postAction() {
|
|
15
35
|
const data = this.post();
|
|
16
36
|
const resp = await this.modelInstance.select({
|
|
@@ -92,12 +112,20 @@ module.exports = class extends BaseRest {
|
|
|
92
112
|
}
|
|
93
113
|
|
|
94
114
|
async putAction() {
|
|
95
|
-
const { display_name, url, avatar, password } = this.post();
|
|
115
|
+
const { display_name, url, avatar, password, type, label } = this.post();
|
|
96
116
|
const { objectId } = this.ctx.state.userInfo;
|
|
97
117
|
const twoFactorAuth = this.post('2fa');
|
|
98
118
|
|
|
99
119
|
const updateData = {};
|
|
100
120
|
|
|
121
|
+
if (this.id && type) {
|
|
122
|
+
updateData.type = type;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (think.isString(label)) {
|
|
126
|
+
updateData.label = label;
|
|
127
|
+
}
|
|
128
|
+
|
|
101
129
|
if (display_name) {
|
|
102
130
|
updateData.display_name = display_name;
|
|
103
131
|
}
|
|
@@ -130,7 +158,9 @@ module.exports = class extends BaseRest {
|
|
|
130
158
|
return this.success();
|
|
131
159
|
}
|
|
132
160
|
|
|
133
|
-
await this.modelInstance.update(updateData, {
|
|
161
|
+
await this.modelInstance.update(updateData, {
|
|
162
|
+
objectId: this.id || objectId,
|
|
163
|
+
});
|
|
134
164
|
|
|
135
165
|
return this.success();
|
|
136
166
|
}
|
package/src/extend/think.js
CHANGED
|
@@ -24,6 +24,10 @@ module.exports = {
|
|
|
24
24
|
},
|
|
25
25
|
promiseAllQueue(promises, taskNum) {
|
|
26
26
|
return new Promise((resolve, reject) => {
|
|
27
|
+
if (!promises.length) {
|
|
28
|
+
return resolve();
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
const ret = [];
|
|
28
32
|
let index = 0;
|
|
29
33
|
let count = 0;
|
|
@@ -55,13 +59,17 @@ module.exports = {
|
|
|
55
59
|
|
|
56
60
|
try {
|
|
57
61
|
const search = helper.promisify(regionSearch.btreeSearch, regionSearch);
|
|
58
|
-
const
|
|
59
|
-
|
|
62
|
+
const result = await search(ip);
|
|
63
|
+
if (!result) {
|
|
64
|
+
return '';
|
|
65
|
+
}
|
|
66
|
+
const { region } = result;
|
|
67
|
+
const [, , province, city, isp] = region.split('|');
|
|
60
68
|
const address = Array.from(new Set([province, city, isp]));
|
|
61
69
|
return address.slice(0, depth).join(' ');
|
|
62
|
-
} catch(e) {
|
|
70
|
+
} catch (e) {
|
|
63
71
|
console.log(e);
|
|
64
72
|
return '';
|
|
65
73
|
}
|
|
66
|
-
}
|
|
74
|
+
},
|
|
67
75
|
};
|
package/src/logic/base.js
CHANGED
|
@@ -8,6 +8,7 @@ module.exports = class extends think.Logic {
|
|
|
8
8
|
`storage/${this.config('storage')}`,
|
|
9
9
|
'Users'
|
|
10
10
|
);
|
|
11
|
+
this.id = this.getId();
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
async __before() {
|
|
@@ -89,4 +90,20 @@ module.exports = class extends think.Logic {
|
|
|
89
90
|
this.ctx.state.userInfo = userInfo;
|
|
90
91
|
this.ctx.state.token = token;
|
|
91
92
|
}
|
|
93
|
+
|
|
94
|
+
getId() {
|
|
95
|
+
const id = this.get('id');
|
|
96
|
+
|
|
97
|
+
if (id && (think.isString(id) || think.isNumber(id))) {
|
|
98
|
+
return id;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const last = decodeURIComponent(this.ctx.path.split('/').pop());
|
|
102
|
+
|
|
103
|
+
if (last !== this.resource && /^([a-z0-9]+,?)*$/i.test(last)) {
|
|
104
|
+
return last;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return '';
|
|
108
|
+
}
|
|
92
109
|
};
|
package/src/logic/user.js
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
const Base = require('./base');
|
|
2
2
|
|
|
3
3
|
module.exports = class extends Base {
|
|
4
|
+
getAction() {
|
|
5
|
+
const { userInfo } = this.ctx.state;
|
|
6
|
+
if (think.isEmpty(userInfo) || userInfo.type !== 'administrator') {
|
|
7
|
+
return this.fail();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
this.rules = {
|
|
11
|
+
page: {
|
|
12
|
+
int: true,
|
|
13
|
+
default: 1,
|
|
14
|
+
},
|
|
15
|
+
pageSize: {
|
|
16
|
+
int: { max: 100 },
|
|
17
|
+
default: 10,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
4
22
|
/**
|
|
5
23
|
* @api {POST} /user user register
|
|
6
24
|
* @apiGroup User
|
|
@@ -30,9 +48,15 @@ module.exports = class extends Base {
|
|
|
30
48
|
* @apiSuccess (200) {String} errmsg return error message if error
|
|
31
49
|
*/
|
|
32
50
|
putAction() {
|
|
51
|
+
// you need login to update yourself profile
|
|
33
52
|
const { userInfo } = this.ctx.state;
|
|
34
53
|
if (think.isEmpty(userInfo)) {
|
|
35
54
|
return this.fail();
|
|
36
55
|
}
|
|
56
|
+
|
|
57
|
+
// you should be a administrator to update otherself info
|
|
58
|
+
if (this.id && userInfo.type !== 'administrator') {
|
|
59
|
+
return this.fail();
|
|
60
|
+
}
|
|
37
61
|
}
|
|
38
62
|
};
|
|
@@ -126,6 +126,95 @@ module.exports = class extends Base {
|
|
|
126
126
|
return data;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
async _getCmtGroupByMailUserIdCache(key, where) {
|
|
130
|
+
if (this.tableName !== 'Comment' || key !== 'user_id_mail') {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const cacheTableName = `cache_group_count_${key}`;
|
|
135
|
+
const currentTableName = this.tableName;
|
|
136
|
+
this.tableName = cacheTableName;
|
|
137
|
+
const cacheData = await this.select({ _complex: where._complex });
|
|
138
|
+
this.tableName = currentTableName;
|
|
139
|
+
return cacheData;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async _setCmtGroupByMailUserIdCache(key, data) {
|
|
143
|
+
if (this.tableName !== 'Comment' || key !== 'user_id_mail') {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const cacheTableName = `cache_group_count_${key}`;
|
|
148
|
+
const currentTableName = this.tableName;
|
|
149
|
+
this.tableName = cacheTableName;
|
|
150
|
+
|
|
151
|
+
await think.promiseAllQueue(
|
|
152
|
+
data.map((item) => {
|
|
153
|
+
if (item.user_id && !think.isString(item.user_id)) {
|
|
154
|
+
item.user_id = item.user_id.toString();
|
|
155
|
+
}
|
|
156
|
+
return this.add(item);
|
|
157
|
+
}),
|
|
158
|
+
1
|
|
159
|
+
);
|
|
160
|
+
this.tableName = currentTableName;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async _updateCmtGroupByMailUserIdCache(data, method) {
|
|
164
|
+
if (this.tableName !== 'Comment') {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!data.user_id && !data.mail) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const cacheTableName = `cache_group_count_user_id_mail`;
|
|
173
|
+
const cacheData = await this.select({
|
|
174
|
+
_complex: {
|
|
175
|
+
_logic: 'or',
|
|
176
|
+
user_id: think.isObject(data.user_id)
|
|
177
|
+
? data.user_id.toString()
|
|
178
|
+
: data.user_id,
|
|
179
|
+
mail: data.mail,
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
if (think.isEmpty(data)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let count = cacheData[0].count;
|
|
187
|
+
switch (method) {
|
|
188
|
+
case 'add':
|
|
189
|
+
if (data.status === 'approved') {
|
|
190
|
+
count += 1;
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
case 'udpate_status':
|
|
194
|
+
if (data.status === 'approved') {
|
|
195
|
+
count += 1;
|
|
196
|
+
} else {
|
|
197
|
+
count -= 1;
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
case 'delete':
|
|
201
|
+
count -= 1;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const currentTableName = this.tableName;
|
|
206
|
+
this.tableName = cacheTableName;
|
|
207
|
+
await this.update({ count }, { objectId: cacheData[0].objectId }).catch(
|
|
208
|
+
(e) => {
|
|
209
|
+
if (e.code === 101) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
throw e;
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
this.tableName = currentTableName;
|
|
216
|
+
}
|
|
217
|
+
|
|
129
218
|
async count(where = {}, options = {}) {
|
|
130
219
|
const instance = this.where(this.tableName, where);
|
|
131
220
|
if (!options.group) {
|
|
@@ -137,7 +226,19 @@ module.exports = class extends Base {
|
|
|
137
226
|
});
|
|
138
227
|
}
|
|
139
228
|
|
|
140
|
-
//
|
|
229
|
+
// get group count cache by group field where data
|
|
230
|
+
const cacheData = await this._getCmtGroupByMailUserIdCache(
|
|
231
|
+
options.group.join('_'),
|
|
232
|
+
where
|
|
233
|
+
);
|
|
234
|
+
const cacheDataMap = {};
|
|
235
|
+
for (let i = 0; i < cacheData.length; i++) {
|
|
236
|
+
const key = options.group
|
|
237
|
+
.map((item) => cacheData[i][item] || undefined)
|
|
238
|
+
.join('_');
|
|
239
|
+
cacheDataMap[key] = cacheData[i];
|
|
240
|
+
}
|
|
241
|
+
|
|
141
242
|
const counts = [];
|
|
142
243
|
const countsPromise = [];
|
|
143
244
|
for (let i = 0; i < options.group.length; i++) {
|
|
@@ -148,10 +249,23 @@ module.exports = class extends Base {
|
|
|
148
249
|
|
|
149
250
|
const groupFlatValue = {};
|
|
150
251
|
options.group.slice(0, i).forEach((group) => {
|
|
151
|
-
groupFlatValue[group] =
|
|
252
|
+
groupFlatValue[group] = undefined;
|
|
152
253
|
});
|
|
153
254
|
|
|
154
255
|
for (let j = 0; j < where._complex[groupName][1].length; j++) {
|
|
256
|
+
const cacheKey = options.group
|
|
257
|
+
.map(
|
|
258
|
+
(item) =>
|
|
259
|
+
({
|
|
260
|
+
...groupFlatValue,
|
|
261
|
+
[groupName]: where._complex[groupName][1][j],
|
|
262
|
+
}[item] || undefined)
|
|
263
|
+
)
|
|
264
|
+
.join('_');
|
|
265
|
+
if (cacheDataMap[cacheKey]) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
|
|
155
269
|
const groupWhere = {
|
|
156
270
|
...where,
|
|
157
271
|
...groupFlatValue,
|
|
@@ -172,8 +286,10 @@ module.exports = class extends Base {
|
|
|
172
286
|
}
|
|
173
287
|
}
|
|
174
288
|
|
|
175
|
-
await think.promiseAllQueue(countsPromise,
|
|
176
|
-
|
|
289
|
+
await think.promiseAllQueue(countsPromise, 1);
|
|
290
|
+
// cache data
|
|
291
|
+
await this._setCmtGroupByMailUserIdCache(options.group.join('_'), counts);
|
|
292
|
+
return [...cacheData, ...counts];
|
|
177
293
|
}
|
|
178
294
|
|
|
179
295
|
async add(
|
|
@@ -190,6 +306,7 @@ module.exports = class extends Base {
|
|
|
190
306
|
instance.setACL(acl);
|
|
191
307
|
|
|
192
308
|
const resp = await instance.save();
|
|
309
|
+
await this._updateCmtGroupByMailUserIdCache(data, 'add');
|
|
193
310
|
return resp.toJSON();
|
|
194
311
|
}
|
|
195
312
|
|
|
@@ -199,11 +316,16 @@ module.exports = class extends Base {
|
|
|
199
316
|
|
|
200
317
|
return Promise.all(
|
|
201
318
|
ret.map(async (item) => {
|
|
319
|
+
const _oldStatus = item.get('status');
|
|
202
320
|
if (think.isFunction(data)) {
|
|
203
321
|
item.set(data(item.toJSON()));
|
|
204
322
|
} else {
|
|
205
323
|
item.set(data);
|
|
206
324
|
}
|
|
325
|
+
const _newStatus = item.get('status');
|
|
326
|
+
if (_newStatus && _oldStatus !== _newStatus) {
|
|
327
|
+
await this._updateCmtGroupByMailUserIdCache(data, 'update_status');
|
|
328
|
+
}
|
|
207
329
|
|
|
208
330
|
const resp = await item.save();
|
|
209
331
|
return resp.toJSON();
|
|
@@ -214,6 +336,7 @@ module.exports = class extends Base {
|
|
|
214
336
|
async delete(where) {
|
|
215
337
|
const instance = this.where(this.tableName, where);
|
|
216
338
|
const data = await instance.find();
|
|
339
|
+
await this._updateCmtGroupByMailUserIdCache(data, 'delete');
|
|
217
340
|
|
|
218
341
|
return AV.Object.destroyAll(data);
|
|
219
342
|
}
|