@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 +3 -4
- package/src/controller/comment.js +5 -0
- package/src/controller/user.js +110 -0
- package/src/locales/zh-CN.json +0 -2
- package/src/locales/zh-TW.json +0 -1
- package/src/logic/user.js +24 -1
- package/src/service/notify.js +12 -5
- package/src/service/storage/leancloud.js +35 -0
- package/src/service/storage/mongodb.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/vercel",
|
|
3
|
-
"version": "1.
|
|
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": "
|
|
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.
|
|
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
|
}
|
package/src/controller/user.js
CHANGED
|
@@ -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
|
};
|
package/src/locales/zh-CN.json
CHANGED
|
@@ -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
|
-
|
package/src/locales/zh-TW.json
CHANGED
|
@@ -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
|
-
|
|
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();
|
package/src/service/notify.js
CHANGED
|
@@ -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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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++) {
|