@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waline/vercel",
3
- "version": "1.16.0",
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": "^12.3.2",
30
- "markdown-it-emoji": "^2.0.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.3",
34
+ "nodemailer": "^6.7.4",
35
35
  "nunjucks": "^3.2.3",
36
36
  "phpass": "^0.1.1",
37
- "prismjs": "^1.27.0",
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",
@@ -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
- disableReigon: DISABLE_REGION && !isFalse(DISABLE_REGION),
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: ['display_name', 'email', 'url', 'type', 'avatar'],
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: ['display_name', 'email', 'url', 'type', 'avatar'],
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: ['display_name', 'email', 'url', 'type', 'avatar'],
332
+ field: [
333
+ 'display_name',
334
+ 'email',
335
+ 'url',
336
+ 'type',
337
+ 'avatar',
338
+ 'label',
339
+ ],
318
340
  }
319
341
  );
320
342
  }
@@ -47,13 +47,18 @@ module.exports = class extends BaseRest {
47
47
  }
48
48
  }
49
49
 
50
- let avatar = user[0].avatar;
51
- if (/(github)/i.test(avatar)) {
52
- avatar =
53
- this.config('avatarProxy') + '?url=' + encodeURIComponent(avatar);
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 = avatar;
56
-
61
+ user[0].avatar = avatarUrl;
57
62
  return this.success({
58
63
  ...user[0],
59
64
  password: null,
@@ -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, { objectId });
161
+ await this.modelInstance.update(updateData, {
162
+ objectId: this.id || objectId,
163
+ });
134
164
 
135
165
  return this.success();
136
166
  }
@@ -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 { region } = await search(ip);
59
- const [,, province, city, isp] = region.split('|');
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
- // todo: query optimize
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] = null;
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, 3);
176
- return counts;
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
  }