@waline/vercel 1.23.5 → 1.24.1

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.23.5",
3
+ "version": "1.24.1",
4
4
  "description": "vercel server for waline comment system",
5
5
  "keywords": [
6
6
  "waline",
@@ -16,7 +16,7 @@
16
16
  "author": "lizheming <i@imnerd.org>",
17
17
  "dependencies": {
18
18
  "@cloudbase/node-sdk": "2.9.1",
19
- "@koa/cors": "3.4.2",
19
+ "@koa/cors": "4.0.0",
20
20
  "akismet": "2.0.7",
21
21
  "deta": "1.1.0",
22
22
  "dompurify": "2.4.0",
@@ -32,7 +32,6 @@
32
32
  "markdown-it-sub": "1.0.0",
33
33
  "markdown-it-sup": "1.0.0",
34
34
  "mathjax-full": "3.2.2",
35
- "mongodb": "4.10.0",
36
35
  "node-fetch": "2.6.7",
37
36
  "nodemailer": "6.8.0",
38
37
  "nunjucks": "3.2.3",
@@ -48,7 +47,7 @@
48
47
  "think-mongo": "2.2.1",
49
48
  "think-router-rest": "1.0.5",
50
49
  "thinkjs": "3.2.14",
51
- "ua-parser-js": "1.0.2"
50
+ "ua-parser-js": "1.0.32"
52
51
  },
53
52
  "engines": {
54
53
  "node": ">=14"
@@ -57,6 +57,11 @@ async function formatCmt(
57
57
  }
58
58
  comment.comment = markdownParser(comment.comment);
59
59
  comment.like = Number(comment.like) || 0;
60
+
61
+ // compat sql storage return number flag to string
62
+ if (typeof comment.sticky === 'string') {
63
+ comment.sticky = Boolean(Number(comment.sticky));
64
+ }
60
65
 
61
66
  return comment;
62
67
  }
@@ -12,6 +12,13 @@ module.exports = class extends BaseRest {
12
12
 
13
13
  async getAction() {
14
14
  const { page, pageSize, email } = this.get();
15
+ const { userInfo } = this.ctx.state;
16
+
17
+ if (think.isEmpty(userInfo) || userInfo.type !== 'administrator') {
18
+ const users = await this.getUsersListByCount();
19
+
20
+ return this.success(users);
21
+ }
15
22
 
16
23
  if (email) {
17
24
  const user = await this.modelInstance.select({ email });
@@ -176,4 +183,107 @@ module.exports = class extends BaseRest {
176
183
 
177
184
  return this.success();
178
185
  }
186
+
187
+ async getUsersListByCount() {
188
+ const { pageSize } = this.get();
189
+ const commentModel = this.service(
190
+ `storage/${this.config('storage')}`,
191
+ 'Comment'
192
+ );
193
+ const counts = await commentModel.count(
194
+ {
195
+ status: ['NOT IN', ['waiting', 'spam']],
196
+ },
197
+ {
198
+ group: ['user_id', 'mail'],
199
+ }
200
+ );
201
+
202
+ counts.sort((a, b) => b.count - a.count);
203
+ counts.length = Math.min(pageSize, counts.length);
204
+
205
+ const userIds = counts
206
+ .filter(({ user_id }) => user_id)
207
+ .map(({ user_id }) => user_id);
208
+
209
+ let usersMap = {};
210
+
211
+ if (userIds.length) {
212
+ const users = await this.modelInstance.select({
213
+ objectId: ['IN', userIds],
214
+ });
215
+
216
+ for (let i = 0; i < users.length; i++) {
217
+ usersMap[users[i].objectId] = users;
218
+ }
219
+ }
220
+
221
+ const users = [];
222
+ const { avatarProxy } = this.config();
223
+
224
+ for (let i = 0; i < counts.length; i++) {
225
+ const count = counts[i];
226
+ const user = {
227
+ count: count.count,
228
+ };
229
+
230
+ if (think.isArray(this.config('levels'))) {
231
+ let level = 0;
232
+
233
+ if (user.count) {
234
+ const _level = think.findLastIndex(
235
+ this.config('levels'),
236
+ (l) => l <= user.count
237
+ );
238
+
239
+ if (_level !== -1) {
240
+ level = _level;
241
+ }
242
+ }
243
+ user.level = level;
244
+ }
245
+
246
+ if (count.user_id && users[count.user_id]) {
247
+ const {
248
+ display_name: nick,
249
+ url: link,
250
+ avatar: avatarUrl,
251
+ label,
252
+ } = users[count.user_id];
253
+ const avatar =
254
+ avatarProxy && !avatarUrl.includes(avatarProxy)
255
+ ? avatarProxy + '?url=' + encodeURIComponent(avatarUrl)
256
+ : avatarUrl;
257
+
258
+ Object.assign(user, { nick, link, avatar, label });
259
+ users.push(user);
260
+ continue;
261
+ }
262
+
263
+ const comments = await commentModel.select(
264
+ { mail: count.mail },
265
+ { limit: 1 }
266
+ );
267
+
268
+ if (think.isEmpty(comments)) {
269
+ continue;
270
+ }
271
+ const comment = comments[0];
272
+
273
+ if (think.isEmpty(comment)) {
274
+ continue;
275
+ }
276
+ const { nick, link } = comment;
277
+ const avatarUrl = await think.service('avatar').stringify(comment);
278
+ const avatar =
279
+ avatarProxy && !avatarUrl.includes(avatarProxy)
280
+ ? avatarProxy + '?url=' + encodeURIComponent(avatarUrl)
281
+ : avatarUrl;
282
+
283
+ Object.assign(user, { nick, link, avatar });
284
+ users.push(user);
285
+ }
286
+
287
+ return users;
288
+ }
179
289
  };
@@ -17,5 +17,3 @@
17
17
  "MAIL_SUBJECT_ADMIN": "{{site.name | safe}} 上有新评论了",
18
18
  "MAIL_TEMPLATE_ADMIN": "<div style='border-top:2px solid #12ADDB;box-shadow:0 1px 3px #AAAAAA;line-height:180%;padding:0 15px 12px;margin:50px auto;font-size:12px;'> <h2 style='border-bottom:1px solid #DDD;font-size:14px;font-weight:normal;padding:13px 0 10px 8px;'> 您在<a style='text-decoration:none;color: #12ADDB;' href='{{site.url}}' target='_blank'>{{site.name}}</a>上的文章有了新的评论 </h2> <p><strong>{{self.nick}}</strong>回复说:</p><div style='background-color: #f5f5f5;padding: 10px 15px;margin:18px 0;word-wrap:break-word;'>{{self.comment | safe}}</div><p>您可以点击<a style='text-decoration:none; color:#12addb' href='{{site.postUrl}}' target='_blank'>查看回复的完整內容</a></p><br/> </div>"
19
19
  }
20
-
21
-
@@ -16,5 +16,4 @@
16
16
  "MAIL_TEMPLATE": "<div style='border-top:2px solid #12ADDB;box-shadow:0 1px 3px #AAAAAA;line-height:180%;padding:0 15px 12px;margin:50px auto;font-size:12px;'> <h2 style='border-bottom:1px solid #DDD;font-size:14px;font-weight:normal;padding:13px 0 10px 8px;'> 您在<a style='text-decoration:none;color: #12ADDB;' href='{{site.url}}' target='_blank'>{{site.name}}</a>上的品論有新的回復 </h2>{{parent.nick}}同學,您層發表評論: <div style='padding:0 12px 0 12px;margin-top:18px'> <div style='background-color: #f5f5f5;padding: 10px 15px;margin:18px 0;word-wrap:break-word;'>{{parent.comment | safe}}</div><p><strong>{{self.nick}}</strong>回復說:</p><div style='background-color: #f5f5f5;padding: 10px 15px;margin:18px 0;word-wrap:break-word;'>{{self.comment | safe}}</div><p>您可以點擊<a style='text-decoration:none; color:#12addb' href='{{site.postUrl}}' target='_blank'>查看回復的完整內容</a>,歡迎再次光臨<a style='text-decoration:none; color:#12addb' href='{{site.url}}' target='_blank'>{{site.name}}</a>。</p><br/> </div></div>",
17
17
  "MAIL_SUBJECT_ADMIN": "{{site.name | safe}} 上有新評論了",
18
18
  "MAIL_TEMPLATE_ADMIN": "<div style='border-top:2px solid #12ADDB;box-shadow:0 1px 3px #AAAAAA;line-height:180%;padding:0 15px 12px;margin:50px auto;font-size:12px;'> <h2 style='border-bottom:1px solid #DDD;font-size:14px;font-weight:normal;padding:13px 0 10px 8px;'> 您在<a style='text-decoration:none;color: #12ADDB;' href='{{site.url}}' target='_blank'>{{site.name}}</a>上的文章有新評論了 </h2> <p><strong>{{self.nick}}</strong>回復說:</p><div style='background-color: #f5f5f5;padding: 10px 15px;margin:18px 0;word-wrap:break-word;'>{{self.comment | safe}}</div><p>您可以點擊<a style='text-decoration:none; color:#12addb' href='{{site.postUrl}}' target='_blank'>查看回復的完整內容</a></p><br/> </div>"
19
-
20
19
  }
package/src/logic/user.js CHANGED
@@ -1,11 +1,28 @@
1
1
  const Base = require('./base');
2
2
 
3
3
  module.exports = class extends Base {
4
+ /**
5
+ * @api {GET} /user user list
6
+ * @apiGroup User
7
+ * @apiVersion 0.0.1
8
+ *
9
+ * @apiParam {String} pageSize page size
10
+ *
11
+ * @apiSuccess (200) {Number} errno 0
12
+ * @apiSuccess (200) {String} errmsg return error message if error
13
+ */
4
14
  getAction() {
5
15
  const { userInfo } = this.ctx.state;
6
16
 
7
17
  if (think.isEmpty(userInfo) || userInfo.type !== 'administrator') {
8
- return this.fail();
18
+ this.rules = {
19
+ pageSize: {
20
+ int: { max: 50 },
21
+ default: 20,
22
+ },
23
+ };
24
+
25
+ return;
9
26
  }
10
27
 
11
28
  this.rules = {
@@ -35,6 +52,12 @@ module.exports = class extends Base {
35
52
  *
36
53
  * @apiSuccess (200) {Number} errno 0
37
54
  * @apiSuccess (200) {String} errmsg return error message if error
55
+ * @apiSuccess (200) {Object[]} data user list
56
+ * @apiSuccess (200) {String} data.nick comment user nick name
57
+ * @apiSuccess (200) {String} data.link comment user link
58
+ * @apiSuccess (200) {String} data.avatar comment user avatar
59
+ * @apiSuccess (200) {String} data.level comment user level
60
+ * @apiSuccess (200) {String} data.label comment user label
38
61
  */
39
62
  postAction() {
40
63
  return this.useCaptchaCheck();
@@ -287,11 +287,18 @@ module.exports = class extends think.Service {
287
287
  form.append('chat_id', TG_CHAT_ID);
288
288
  form.append('parse_mode', 'MarkdownV2');
289
289
 
290
- return fetch(`https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage`, {
291
- method: 'POST',
292
- header: form.getHeaders(),
293
- body: form,
294
- }).then((resp) => resp.json());
290
+ const resp = await fetch(
291
+ `https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage`,
292
+ {
293
+ method: 'POST',
294
+ header: form.getHeaders(),
295
+ body: form,
296
+ }
297
+ ).then((resp) => resp.json());
298
+
299
+ if (!resp.ok) {
300
+ throw new Error(resp.description);
301
+ }
295
302
  }
296
303
 
297
304
  async pushplus({ title, content }, self, parent) {
@@ -260,6 +260,41 @@ module.exports = class extends Base {
260
260
  options.group.join('_'),
261
261
  where
262
262
  );
263
+
264
+ if (!where._complex) {
265
+ if (cacheData.length) {
266
+ return cacheData;
267
+ }
268
+
269
+ const counts = await this.select(where, { field: options.group });
270
+ const countsMap = {};
271
+
272
+ for (let i = 0; i < counts.length; i++) {
273
+ const key = options.group
274
+ .map((item) => counts[i][item] || undefined)
275
+ .join('_');
276
+
277
+ if (!countsMap[key]) {
278
+ countsMap[key] = {};
279
+
280
+ for (let j = 0; j < options.group.length; j++) {
281
+ const field = options.group[j];
282
+
283
+ countsMap[key][field] = counts[i][field];
284
+ }
285
+ countsMap[key].count = 0;
286
+ }
287
+ countsMap[key].count += 1;
288
+ }
289
+
290
+ const ret = Object.values(countsMap);
291
+
292
+ // cache data
293
+ await this._setCmtGroupByMailUserIdCache(options.group.join('_'), ret);
294
+
295
+ return ret;
296
+ }
297
+
263
298
  const cacheDataMap = {};
264
299
 
265
300
  for (let i = 0; i < cacheData.length; i++) {
@@ -1,4 +1,4 @@
1
- const { ObjectId } = require('mongodb');
1
+ const { ObjectID: ObjectId } = require('think-mongo/lib/model');
2
2
  const Base = require('./base');
3
3
 
4
4
  module.exports = class extends Base {